analytica-frontend-lib 1.6.21 → 1.6.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (405) hide show
  1. package/dist/AccessReportModal/index.js +1 -1
  2. package/dist/AccessReportModal/index.mjs +1 -1
  3. package/dist/AccessibilityWidget/index.js +1 -1
  4. package/dist/AccessibilityWidget/index.mjs +1 -1
  5. package/dist/Accordation/index.js +1 -1
  6. package/dist/Accordation/index.mjs +1 -1
  7. package/dist/ActivitiesHistory/index.js +19 -19
  8. package/dist/ActivitiesHistory/index.mjs +18 -18
  9. package/dist/ActivityCardQuestionBanks/index.js +19 -19
  10. package/dist/ActivityCardQuestionBanks/index.mjs +18 -18
  11. package/dist/ActivityCardQuestionPreview/index.js +19 -19
  12. package/dist/ActivityCardQuestionPreview/index.mjs +18 -18
  13. package/dist/ActivityCreate/ActivityCreate.utils/index.js +3 -3
  14. package/dist/ActivityCreate/ActivityCreate.utils/index.mjs +2 -2
  15. package/dist/ActivityCreate/ActivityCreateContent/index.js +19 -19
  16. package/dist/ActivityCreate/ActivityCreateContent/index.mjs +18 -18
  17. package/dist/ActivityCreate/index.js +19 -19
  18. package/dist/ActivityCreate/index.mjs +18 -18
  19. package/dist/ActivityDetails/index.js +19 -19
  20. package/dist/ActivityDetails/index.mjs +18 -18
  21. package/dist/ActivityFilters/index.js +19 -19
  22. package/dist/ActivityFilters/index.mjs +18 -18
  23. package/dist/ActivityPageLayout/index.js +19 -19
  24. package/dist/ActivityPageLayout/index.mjs +18 -18
  25. package/dist/ActivityPreview/index.js +19 -19
  26. package/dist/ActivityPreview/index.mjs +18 -18
  27. package/dist/Alert/index.js +1 -1
  28. package/dist/Alert/index.mjs +1 -1
  29. package/dist/AlertDialog/index.js +1 -1
  30. package/dist/AlertDialog/index.mjs +1 -1
  31. package/dist/AlertManager/index.js +19 -19
  32. package/dist/AlertManager/index.mjs +18 -18
  33. package/dist/AlertManagerView/index.js +1 -1
  34. package/dist/AlertManagerView/index.mjs +1 -1
  35. package/dist/Alternative/index.js +1 -1
  36. package/dist/Alternative/index.mjs +1 -1
  37. package/dist/AnnouncementModal/index.js +1 -1
  38. package/dist/AnnouncementModal/index.mjs +1 -1
  39. package/dist/AnnouncementsGate/index.js +1 -1
  40. package/dist/AnnouncementsGate/index.mjs +1 -1
  41. package/dist/AnswerKeyModal/index.js +1 -1
  42. package/dist/AnswerKeyModal/index.mjs +1 -1
  43. package/dist/AppHeader/index.js +1 -1
  44. package/dist/AppHeader/index.mjs +1 -1
  45. package/dist/AppLayout/index.js +1 -1
  46. package/dist/AppLayout/index.mjs +1 -1
  47. package/dist/Avatar/index.js +1 -1
  48. package/dist/Avatar/index.mjs +1 -1
  49. package/dist/Badge/index.js +1 -1
  50. package/dist/Badge/index.mjs +1 -1
  51. package/dist/BreadcrumbMenu/index.js +1 -1
  52. package/dist/BreadcrumbMenu/index.mjs +1 -1
  53. package/dist/Button/index.js +1 -1
  54. package/dist/Button/index.mjs +1 -1
  55. package/dist/Calendar/index.js +1 -1
  56. package/dist/Calendar/index.mjs +1 -1
  57. package/dist/CalendarCard/index.js +1 -1
  58. package/dist/CalendarCard/index.mjs +1 -1
  59. package/dist/Card/index.js +1 -1
  60. package/dist/Card/index.mjs +1 -1
  61. package/dist/ChartComponents/index.js +1 -1
  62. package/dist/ChartComponents/index.mjs +1 -1
  63. package/dist/Chat/index.js +1 -1
  64. package/dist/Chat/index.mjs +1 -1
  65. package/dist/Chatbot/index.js +1 -1
  66. package/dist/Chatbot/index.mjs +1 -1
  67. package/dist/CheckBox/index.js +1 -1
  68. package/dist/CheckBox/index.mjs +1 -1
  69. package/dist/CheckBoxGroup/index.js +19 -19
  70. package/dist/CheckBoxGroup/index.mjs +18 -18
  71. package/dist/Chips/index.js +1 -1
  72. package/dist/Chips/index.mjs +1 -1
  73. package/dist/ChoroplethMap/index.js +1 -1
  74. package/dist/ChoroplethMap/index.mjs +1 -1
  75. package/dist/ColorPicker/index.js +1 -1
  76. package/dist/ColorPicker/index.mjs +1 -1
  77. package/dist/ComparatorChart/ComparatorView/index.js +1 -1
  78. package/dist/ComparatorChart/ComparatorView/index.mjs +1 -1
  79. package/dist/CorrectActivityModal/index.js +6 -6
  80. package/dist/CorrectActivityModal/index.mjs +5 -5
  81. package/dist/Divider/index.js +1 -1
  82. package/dist/Divider/index.mjs +1 -1
  83. package/dist/DownloadButton/index.js +1 -1
  84. package/dist/DownloadButton/index.mjs +1 -1
  85. package/dist/DownloadModal/index.js +1 -1
  86. package/dist/DownloadModal/index.mjs +1 -1
  87. package/dist/DragHandleButton/index.js +1 -1
  88. package/dist/DragHandleButton/index.mjs +1 -1
  89. package/dist/DropdownMenu/index.js +1 -1
  90. package/dist/DropdownMenu/index.mjs +1 -1
  91. package/dist/EmptyState/index.js +1 -1
  92. package/dist/EmptyState/index.mjs +1 -1
  93. package/dist/EnemClassroomStartModal/index.d.ts +0 -2
  94. package/dist/EnemClassroomStartModal/index.d.ts.map +1 -1
  95. package/dist/EnemClassroomStartModal/index.js +4 -6
  96. package/dist/EnemClassroomStartModal/index.js.map +1 -1
  97. package/dist/EnemClassroomStartModal/index.mjs +3 -5
  98. package/dist/EssayThemePicker/index.js +1 -1
  99. package/dist/EssayThemePicker/index.mjs +1 -1
  100. package/dist/ExamDetailsLayout/ExamDetailsPage/index.js +19 -19
  101. package/dist/ExamDetailsLayout/ExamDetailsPage/index.mjs +18 -18
  102. package/dist/ExamPageLayout/examsTableConfig/index.js +1 -1
  103. package/dist/ExamPageLayout/examsTableConfig/index.mjs +1 -1
  104. package/dist/ExamPageLayout/index.js +19 -19
  105. package/dist/ExamPageLayout/index.mjs +18 -18
  106. package/dist/FileAttachment/index.js +1 -1
  107. package/dist/FileAttachment/index.mjs +1 -1
  108. package/dist/FileDropzone/index.js +1 -1
  109. package/dist/FileDropzone/index.mjs +1 -1
  110. package/dist/Forum/index.js +4 -4
  111. package/dist/Forum/index.mjs +3 -3
  112. package/dist/HtmlMathRenderer/index.js +1 -1
  113. package/dist/HtmlMathRenderer/index.mjs +1 -1
  114. package/dist/IconButton/index.js +1 -1
  115. package/dist/IconButton/index.mjs +1 -1
  116. package/dist/IconRoundedButton/index.js +1 -1
  117. package/dist/IconRoundedButton/index.mjs +1 -1
  118. package/dist/ImagePreviewCard/index.js +1 -1
  119. package/dist/ImagePreviewCard/index.mjs +1 -1
  120. package/dist/ImageUpload/index.js +1 -1
  121. package/dist/ImageUpload/index.mjs +1 -1
  122. package/dist/LatexRenderer/index.js +1 -1
  123. package/dist/LatexRenderer/index.mjs +1 -1
  124. package/dist/LevelBar/index.js +1 -1
  125. package/dist/LevelBar/index.mjs +1 -1
  126. package/dist/Menu/index.js +1 -1
  127. package/dist/Menu/index.mjs +1 -1
  128. package/dist/MetricBox/index.js +1 -1
  129. package/dist/MetricBox/index.mjs +1 -1
  130. package/dist/Modal/index.js +1 -1
  131. package/dist/Modal/index.mjs +1 -1
  132. package/dist/ModalComponents/index.js +1 -1
  133. package/dist/ModalComponents/index.mjs +1 -1
  134. package/dist/MultiSearchSelect/index.js +1 -1
  135. package/dist/MultiSearchSelect/index.mjs +1 -1
  136. package/dist/MultipleChoice/index.js +1 -1
  137. package/dist/MultipleChoice/index.mjs +1 -1
  138. package/dist/NavButton/index.js +1 -1
  139. package/dist/NavButton/index.mjs +1 -1
  140. package/dist/NoSearchResult/index.js +1 -1
  141. package/dist/NoSearchResult/index.mjs +1 -1
  142. package/dist/NotFound/index.js +1 -1
  143. package/dist/NotFound/index.mjs +1 -1
  144. package/dist/NotificationCard/index.js +1 -1
  145. package/dist/NotificationCard/index.mjs +1 -1
  146. package/dist/PerformanceDistributionChart/index.js +1 -1
  147. package/dist/PerformanceDistributionChart/index.mjs +1 -1
  148. package/dist/PerformanceQuestionsData/index.js +1 -1
  149. package/dist/PerformanceQuestionsData/index.mjs +1 -1
  150. package/dist/PerformanceRanking/index.js +1 -1
  151. package/dist/PerformanceRanking/index.mjs +1 -1
  152. package/dist/PerformanceReport/index.js +1 -1
  153. package/dist/PerformanceReport/index.mjs +1 -1
  154. package/dist/PerformanceReportModal/index.js +1 -1
  155. package/dist/PerformanceReportModal/index.mjs +1 -1
  156. package/dist/PeriodSelector/index.js +1 -1
  157. package/dist/PeriodSelector/index.mjs +1 -1
  158. package/dist/PrintableUsersTable/index.js +19 -19
  159. package/dist/PrintableUsersTable/index.mjs +18 -18
  160. package/dist/ProficiencyChart/index.js +1 -1
  161. package/dist/ProficiencyChart/index.mjs +1 -1
  162. package/dist/ProgressBar/index.js +1 -1
  163. package/dist/ProgressBar/index.mjs +1 -1
  164. package/dist/ProgressCircle/index.js +1 -1
  165. package/dist/ProgressCircle/index.mjs +1 -1
  166. package/dist/ProgressModal/index.js +1 -1
  167. package/dist/ProgressModal/index.mjs +1 -1
  168. package/dist/QuestionsData/index.js +1 -1
  169. package/dist/QuestionsData/index.mjs +1 -1
  170. package/dist/QuestionsPerformanceCard/index.js +19 -19
  171. package/dist/QuestionsPerformanceCard/index.mjs +18 -18
  172. package/dist/Quiz/QuizResult/index.d.ts.map +1 -1
  173. package/dist/Quiz/QuizResult/index.js +4 -4
  174. package/dist/Quiz/QuizResult/index.mjs +3 -3
  175. package/dist/Quiz/TeacherFeedbackSection/index.js +3 -3
  176. package/dist/Quiz/TeacherFeedbackSection/index.mjs +2 -2
  177. package/dist/Quiz/TeacherQuestionComment/index.js +3 -3
  178. package/dist/Quiz/TeacherQuestionComment/index.mjs +2 -2
  179. package/dist/Quiz/index.d.ts.map +1 -1
  180. package/dist/Quiz/index.js +6 -6
  181. package/dist/Quiz/index.mjs +5 -5
  182. package/dist/Quiz/useQuizStore/index.d.ts +6 -0
  183. package/dist/Quiz/useQuizStore/index.d.ts.map +1 -1
  184. package/dist/Quiz/useQuizStore/index.js +2 -2
  185. package/dist/Quiz/useQuizStore/index.mjs +1 -1
  186. package/dist/QuizTimer/index.js +3 -3
  187. package/dist/QuizTimer/index.mjs +2 -2
  188. package/dist/Radio/index.js +1 -1
  189. package/dist/Radio/index.mjs +1 -1
  190. package/dist/RangeGauge/index.js +1 -1
  191. package/dist/RangeGauge/index.mjs +1 -1
  192. package/dist/RecommendedLessonCreate/index.js +19 -19
  193. package/dist/RecommendedLessonCreate/index.mjs +18 -18
  194. package/dist/RecommendedLessonDetails/index.js +1 -1
  195. package/dist/RecommendedLessonDetails/index.mjs +1 -1
  196. package/dist/RecommendedLessonsHistory/index.js +19 -19
  197. package/dist/RecommendedLessonsHistory/index.mjs +18 -18
  198. package/dist/ReportDetailModal/index.js +1 -1
  199. package/dist/ReportDetailModal/index.mjs +1 -1
  200. package/dist/RestrictedAccess/index.js +1 -1
  201. package/dist/RestrictedAccess/index.mjs +1 -1
  202. package/dist/RichEditor/index.js +3 -3
  203. package/dist/RichEditor/index.mjs +2 -2
  204. package/dist/{RichEditorCore-F5QPQLEK.js → RichEditorCore-NEIZKFKE.js} +2 -2
  205. package/dist/{RichEditorCore-F5QPQLEK.js.map → RichEditorCore-NEIZKFKE.js.map} +1 -1
  206. package/dist/{RichEditorCore-QZYPHNRY.mjs → RichEditorCore-TLAFZ5XG.mjs} +2 -2
  207. package/dist/ScoreCircle/index.js +1 -1
  208. package/dist/ScoreCircle/index.mjs +1 -1
  209. package/dist/Search/index.js +1 -1
  210. package/dist/Search/index.mjs +1 -1
  211. package/dist/SearchSelect/index.js +1 -1
  212. package/dist/SearchSelect/index.mjs +1 -1
  213. package/dist/Select/index.js +1 -1
  214. package/dist/Select/index.mjs +1 -1
  215. package/dist/SelectionButton/index.js +1 -1
  216. package/dist/SelectionButton/index.mjs +1 -1
  217. package/dist/SendActivityModal/SendActivityModal.js +19 -19
  218. package/dist/SendActivityModal/SendActivityModal.mjs +18 -18
  219. package/dist/SendActivityModal/index.js +19 -19
  220. package/dist/SendActivityModal/index.mjs +18 -18
  221. package/dist/SendLessonModal/index.js +19 -19
  222. package/dist/SendLessonModal/index.mjs +18 -18
  223. package/dist/SimpleBarChart/index.js +1 -1
  224. package/dist/SimpleBarChart/index.mjs +1 -1
  225. package/dist/SimulatedPerformance/SimulatedPerformanceView/index.js +19 -19
  226. package/dist/SimulatedPerformance/SimulatedPerformanceView/index.mjs +18 -18
  227. package/dist/SimulatedPerformance/useSimulatedPerformance/index.js +1 -1
  228. package/dist/SimulatedPerformance/useSimulatedPerformance/index.mjs +1 -1
  229. package/dist/SimulatedStudentSimulationsModal/index.js +19 -19
  230. package/dist/SimulatedStudentSimulationsModal/index.mjs +5 -5
  231. package/dist/SimulationsPage/index.js +19 -19
  232. package/dist/SimulationsPage/index.mjs +18 -18
  233. package/dist/Skeleton/index.js +1 -1
  234. package/dist/Skeleton/index.mjs +1 -1
  235. package/dist/StatisticsCard/index.js +1 -1
  236. package/dist/StatisticsCard/index.mjs +1 -1
  237. package/dist/Stepper/index.js +1 -1
  238. package/dist/Stepper/index.mjs +1 -1
  239. package/dist/StudentLessonProgressModal/index.js +1 -1
  240. package/dist/StudentLessonProgressModal/index.mjs +1 -1
  241. package/dist/StudentPerformanceDetailsModal/index.js +1 -1
  242. package/dist/StudentPerformanceDetailsModal/index.mjs +1 -1
  243. package/dist/StudentRanking/index.js +1 -1
  244. package/dist/StudentRanking/index.mjs +1 -1
  245. package/dist/SubjectIcons/index.js +1 -1
  246. package/dist/SubjectIcons/index.mjs +1 -1
  247. package/dist/SubjectMenuOverflow/index.js +1 -1
  248. package/dist/SubjectMenuOverflow/index.mjs +1 -1
  249. package/dist/Support/TicketModal/index.js +1 -1
  250. package/dist/Support/TicketModal/index.mjs +1 -1
  251. package/dist/Support/index.js +1 -1
  252. package/dist/Support/index.mjs +1 -1
  253. package/dist/Table/TablePagination/index.js +1 -1
  254. package/dist/Table/TablePagination/index.mjs +1 -1
  255. package/dist/Table/index.js +1 -1
  256. package/dist/Table/index.mjs +1 -1
  257. package/dist/TableProvider/index.js +19 -19
  258. package/dist/TableProvider/index.mjs +18 -18
  259. package/dist/Text/index.js +1 -1
  260. package/dist/Text/index.mjs +1 -1
  261. package/dist/TextArea/index.js +1 -1
  262. package/dist/TextArea/index.mjs +1 -1
  263. package/dist/ThemeToggle/index.js +1 -1
  264. package/dist/ThemeToggle/index.mjs +1 -1
  265. package/dist/TimeChart/index.js +1 -1
  266. package/dist/TimeChart/index.mjs +1 -1
  267. package/dist/TimeReport/index.js +1 -1
  268. package/dist/TimeReport/index.mjs +1 -1
  269. package/dist/Toast/Toaster/index.js +1 -1
  270. package/dist/Toast/Toaster/index.mjs +1 -1
  271. package/dist/Toast/index.js +1 -1
  272. package/dist/Toast/index.mjs +1 -1
  273. package/dist/ToggleSwitch/index.js +1 -1
  274. package/dist/ToggleSwitch/index.mjs +1 -1
  275. package/dist/TokenValidation/index.js +1 -1
  276. package/dist/TokenValidation/index.mjs +1 -1
  277. package/dist/Tooltip/index.js +1 -1
  278. package/dist/Tooltip/index.mjs +1 -1
  279. package/dist/TruncatedText/index.js +1 -1
  280. package/dist/TruncatedText/index.mjs +1 -1
  281. package/dist/TypeSelector/index.js +1 -1
  282. package/dist/TypeSelector/index.mjs +1 -1
  283. package/dist/UnifiedDraftModelPage/index.js +19 -19
  284. package/dist/UnifiedDraftModelPage/index.mjs +18 -18
  285. package/dist/UnifiedHistoryPage/index.js +19 -19
  286. package/dist/UnifiedHistoryPage/index.mjs +18 -18
  287. package/dist/VerticalBarChart/index.js +1 -1
  288. package/dist/VerticalBarChart/index.mjs +1 -1
  289. package/dist/VideoPlayer/index.js +1 -1
  290. package/dist/VideoPlayer/index.mjs +1 -1
  291. package/dist/Whiteboard/index.js +1 -1
  292. package/dist/Whiteboard/index.mjs +1 -1
  293. package/dist/{chunk-JBAGQ7L5.js → chunk-3VG5MEMY.js} +14 -13
  294. package/dist/chunk-3VG5MEMY.js.map +1 -0
  295. package/dist/{chunk-ED23HOE6.mjs → chunk-5NIU4XJY.mjs} +7 -6
  296. package/dist/chunk-5NIU4XJY.mjs.map +1 -0
  297. package/dist/{chunk-DDZDNGBU.mjs → chunk-64WGFT5E.mjs} +3 -1
  298. package/dist/chunk-64WGFT5E.mjs.map +1 -0
  299. package/dist/{chunk-QN32O3J3.js → chunk-6MK23BTK.js} +62 -62
  300. package/dist/{chunk-QN32O3J3.js.map → chunk-6MK23BTK.js.map} +1 -1
  301. package/dist/{chunk-XX4WE4TL.js → chunk-726PIA5M.js} +1 -1
  302. package/dist/chunk-726PIA5M.js.map +1 -0
  303. package/dist/{chunk-VZJKHIHN.mjs → chunk-7KALNPIG.mjs} +4 -4
  304. package/dist/{chunk-NQNEG4KR.mjs → chunk-7VKFZHWP.mjs} +11 -10
  305. package/dist/chunk-7VKFZHWP.mjs.map +1 -0
  306. package/dist/{chunk-GYQUB3VW.js → chunk-BBFV3NDF.js} +8 -6
  307. package/dist/chunk-BBFV3NDF.js.map +1 -0
  308. package/dist/{chunk-I3Z2BHGN.js → chunk-BJHYT75K.js} +3 -3
  309. package/dist/{chunk-I3Z2BHGN.js.map → chunk-BJHYT75K.js.map} +1 -1
  310. package/dist/{chunk-CFTQ24UT.mjs → chunk-CMYBNZ74.mjs} +2 -2
  311. package/dist/{chunk-R7NS5KFE.mjs → chunk-CWUNF7U6.mjs} +3 -3
  312. package/dist/{chunk-NAOHJW6H.mjs → chunk-DLHV7CZN.mjs} +6 -6
  313. package/dist/{chunk-7EXSCXPK.mjs → chunk-DMYETMZG.mjs} +2 -2
  314. package/dist/{chunk-RQPSA4QV.js → chunk-GQUTHDV2.js} +3 -3
  315. package/dist/{chunk-RQPSA4QV.js.map → chunk-GQUTHDV2.js.map} +1 -1
  316. package/dist/{chunk-OZWPOXVU.mjs → chunk-GVGDUCVF.mjs} +2 -2
  317. package/dist/{chunk-QFWY46XU.js → chunk-IMDS5NQL.js} +16 -16
  318. package/dist/{chunk-QFWY46XU.js.map → chunk-IMDS5NQL.js.map} +1 -1
  319. package/dist/{chunk-KYGFYAU7.js → chunk-JHN35HPH.js} +3 -3
  320. package/dist/{chunk-KYGFYAU7.js.map → chunk-JHN35HPH.js.map} +1 -1
  321. package/dist/{chunk-DMZTDCUF.js → chunk-JPQI2RIV.js} +3 -3
  322. package/dist/{chunk-DMZTDCUF.js.map → chunk-JPQI2RIV.js.map} +1 -1
  323. package/dist/{chunk-LXX5KU3O.mjs → chunk-LMDAF6LA.mjs} +2 -2
  324. package/dist/{chunk-FL4W4S6J.mjs → chunk-MOSP7FR5.mjs} +1 -1
  325. package/dist/chunk-MOSP7FR5.mjs.map +1 -0
  326. package/dist/{chunk-HZXXADG4.js → chunk-NLPBW3SW.js} +44 -43
  327. package/dist/chunk-NLPBW3SW.js.map +1 -0
  328. package/dist/{chunk-TAABXGZL.mjs → chunk-O3JP6ZMQ.mjs} +2 -2
  329. package/dist/{chunk-MFO4S7T5.js → chunk-PJOBOBNJ.js} +3 -3
  330. package/dist/{chunk-MFO4S7T5.js.map → chunk-PJOBOBNJ.js.map} +1 -1
  331. package/dist/{chunk-CWGYVJTQ.mjs → chunk-PORNR2NU.mjs} +3 -3
  332. package/dist/chunk-QF3UKRIJ.js +33 -0
  333. package/dist/chunk-QF3UKRIJ.js.map +1 -0
  334. package/dist/chunk-RAHXO5YU.mjs +33 -0
  335. package/dist/chunk-RAHXO5YU.mjs.map +1 -0
  336. package/dist/{chunk-BTZANNVO.js → chunk-RQ65U5G2.js} +3 -3
  337. package/dist/{chunk-BTZANNVO.js.map → chunk-RQ65U5G2.js.map} +1 -1
  338. package/dist/{chunk-Q56I63FO.mjs → chunk-RVGLLMWG.mjs} +2 -2
  339. package/dist/{chunk-VVRRAF6H.js → chunk-S2S7TK3L.js} +49 -36
  340. package/dist/chunk-S2S7TK3L.js.map +1 -0
  341. package/dist/{chunk-INIH23HU.js → chunk-VF6QXUX3.js} +2 -2
  342. package/dist/{chunk-INIH23HU.js.map → chunk-VF6QXUX3.js.map} +1 -1
  343. package/dist/{chunk-QWC5GC2Q.mjs → chunk-WY7V5UJN.mjs} +2 -2
  344. package/dist/{chunk-OCKO7T7L.js → chunk-XQAYZBLL.js} +3 -3
  345. package/dist/{chunk-OCKO7T7L.js.map → chunk-XQAYZBLL.js.map} +1 -1
  346. package/dist/{chunk-ATGLKKXI.js → chunk-Y7ZBWTD7.js} +17 -17
  347. package/dist/{chunk-ATGLKKXI.js.map → chunk-Y7ZBWTD7.js.map} +1 -1
  348. package/dist/{chunk-7XWWI5EY.js → chunk-YFIB3YNQ.js} +11 -11
  349. package/dist/{chunk-7XWWI5EY.js.map → chunk-YFIB3YNQ.js.map} +1 -1
  350. package/dist/{chunk-UCWTF336.mjs → chunk-YWGJAB63.mjs} +2 -2
  351. package/dist/{chunk-4MPXVPBQ.mjs → chunk-YYHS2TM6.mjs} +43 -30
  352. package/dist/chunk-YYHS2TM6.mjs.map +1 -0
  353. package/dist/components/Quiz/Quiz.utils.d.ts +8 -0
  354. package/dist/components/Quiz/Quiz.utils.d.ts.map +1 -1
  355. package/dist/hooks/useAppContent/index.js +19 -19
  356. package/dist/hooks/useAppContent/index.mjs +18 -18
  357. package/dist/hooks/useComparator/index.js +1 -1
  358. package/dist/hooks/useComparator/index.mjs +1 -1
  359. package/dist/hooks/useDraftAutoSave/index.js +3 -3
  360. package/dist/hooks/useDraftAutoSave/index.mjs +2 -2
  361. package/dist/index.js +19 -19
  362. package/dist/index.mjs +18 -18
  363. package/dist/types/enemClassroom/index.d.ts +29 -2
  364. package/dist/types/enemClassroom/index.d.ts.map +1 -1
  365. package/dist/types/enemClassroom/index.js +4 -2
  366. package/dist/types/enemClassroom/index.js.map +1 -1
  367. package/dist/types/enemClassroom/index.mjs +3 -1
  368. package/dist/types/enemClassroom.d.ts +29 -2
  369. package/dist/types/enemClassroom.d.ts.map +1 -1
  370. package/dist/types/questionTypes/index.js +1 -1
  371. package/dist/types/questionTypes/index.mjs +1 -1
  372. package/dist/utils/index.js +1 -1
  373. package/dist/utils/index.mjs +1 -1
  374. package/dist/utils/renderSubjectCell/index.js +1 -1
  375. package/dist/utils/renderSubjectCell/index.mjs +1 -1
  376. package/dist/utils/renderTextCell/index.js +1 -1
  377. package/dist/utils/renderTextCell/index.mjs +1 -1
  378. package/package.json +1 -1
  379. package/dist/chunk-4MPXVPBQ.mjs.map +0 -1
  380. package/dist/chunk-DDZDNGBU.mjs.map +0 -1
  381. package/dist/chunk-ED23HOE6.mjs.map +0 -1
  382. package/dist/chunk-FL4W4S6J.mjs.map +0 -1
  383. package/dist/chunk-GYQUB3VW.js.map +0 -1
  384. package/dist/chunk-HZXXADG4.js.map +0 -1
  385. package/dist/chunk-JBAGQ7L5.js.map +0 -1
  386. package/dist/chunk-ND5M3DZA.mjs +0 -21
  387. package/dist/chunk-ND5M3DZA.mjs.map +0 -1
  388. package/dist/chunk-NQNEG4KR.mjs.map +0 -1
  389. package/dist/chunk-NYKBCQUO.js +0 -21
  390. package/dist/chunk-NYKBCQUO.js.map +0 -1
  391. package/dist/chunk-VVRRAF6H.js.map +0 -1
  392. package/dist/chunk-XX4WE4TL.js.map +0 -1
  393. /package/dist/{RichEditorCore-QZYPHNRY.mjs.map → RichEditorCore-TLAFZ5XG.mjs.map} +0 -0
  394. /package/dist/{chunk-VZJKHIHN.mjs.map → chunk-7KALNPIG.mjs.map} +0 -0
  395. /package/dist/{chunk-CFTQ24UT.mjs.map → chunk-CMYBNZ74.mjs.map} +0 -0
  396. /package/dist/{chunk-R7NS5KFE.mjs.map → chunk-CWUNF7U6.mjs.map} +0 -0
  397. /package/dist/{chunk-NAOHJW6H.mjs.map → chunk-DLHV7CZN.mjs.map} +0 -0
  398. /package/dist/{chunk-7EXSCXPK.mjs.map → chunk-DMYETMZG.mjs.map} +0 -0
  399. /package/dist/{chunk-OZWPOXVU.mjs.map → chunk-GVGDUCVF.mjs.map} +0 -0
  400. /package/dist/{chunk-LXX5KU3O.mjs.map → chunk-LMDAF6LA.mjs.map} +0 -0
  401. /package/dist/{chunk-TAABXGZL.mjs.map → chunk-O3JP6ZMQ.mjs.map} +0 -0
  402. /package/dist/{chunk-CWGYVJTQ.mjs.map → chunk-PORNR2NU.mjs.map} +0 -0
  403. /package/dist/{chunk-Q56I63FO.mjs.map → chunk-RVGLLMWG.mjs.map} +0 -0
  404. /package/dist/{chunk-QWC5GC2Q.mjs.map → chunk-WY7V5UJN.mjs.map} +0 -0
  405. /package/dist/{chunk-UCWTF336.mjs.map → chunk-YWGJAB63.mjs.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-ATGLKKXI.js","../src/components/CorrectActivityModal/CorrectActivityModal.tsx","../src/utils/questionRenderer/alternative/index.tsx","../src/utils/questionRenderer/multipleChoice/index.tsx","../src/utils/questionRenderer/trueOrFalse/index.tsx","../src/utils/questionRenderer/dissertative/index.tsx","../src/utils/questionRenderer/fill/index.tsx","../src/utils/questionRenderer/image/index.tsx","../src/utils/questionRenderer/connectDots/index.tsx","../src/utils/questionRenderer/index.tsx"],"names":["jsx","jsxs","Fragment"],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-ATGLKKXI.js","sourcesContent":[null,"import {\n useState,\n useRef,\n useEffect,\n useCallback,\n type ReactNode,\n} from 'react';\nimport { PencilSimpleIcon } from '@phosphor-icons/react/dist/csr/PencilSimple';\nimport { PaperclipIcon } from '@phosphor-icons/react/dist/csr/Paperclip';\nimport { XIcon } from '@phosphor-icons/react/dist/csr/X';\nimport { ImageIcon } from '@phosphor-icons/react/dist/csr/Image';\nimport Modal from '../Modal/Modal';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport Badge from '../Badge/Badge';\nimport Radio from '../Radio/Radio';\nimport TextArea from '../TextArea/TextArea';\nimport { CardAccordation, AccordionGroup } from '../Accordation';\nimport { generateFileId } from '../FileAttachment/FileAttachment';\nimport type { AttachedFile } from '../FileAttachment/FileAttachment';\nimport { StatCard } from '../shared/StatCard';\nimport { QuestionCommentField } from '../shared/QuestionCommentField';\nimport { cn } from '../../utils/utils';\nimport {\n AI_CORRECTION_STATUS,\n CORRECTION_SOURCE,\n QUESTION_TYPE,\n} from '../Quiz/useQuizStore';\nimport CorrectionSourceTag from '../Quiz/CorrectionSourceTag';\nimport { resolveCorrectionText } from '../Quiz/correction.utils';\nimport {\n type StudentActivityCorrectionData,\n type SaveQuestionCorrectionPayload,\n type SaveQuestionCommentPayload,\n getQuestionStatusBadgeConfig,\n getQuestionStatusFromData,\n QUESTION_STATUS,\n} from '../../utils/studentActivityCorrection';\nimport {\n renderQuestionAlternative,\n renderQuestionMultipleChoice,\n renderQuestionTrueOrFalse,\n renderQuestionDissertative,\n renderQuestionFill,\n renderQuestionImage,\n renderQuestionConnectDots,\n} from '../../utils/questionRenderer/index';\nimport useToastStore from '../Toast/utils/ToastStore';\nimport { HtmlMathRenderer } from '../HtmlMathRenderer';\n\n/**\n * Props for the CorrectActivityModal component\n */\nexport interface CorrectActivityModalProps {\n /** Whether the modal is open */\n isOpen: boolean;\n /** Function to close the modal */\n onClose: () => void;\n /** Student activity correction data */\n data: StudentActivityCorrectionData | null;\n /** Whether the modal is in view-only mode */\n isViewOnly?: boolean;\n /** Callback when observation is submitted with optional files */\n onObservationSubmit?: (\n studentId: string,\n observation: string,\n files: File[],\n existingAttachment: string | null\n ) => void;\n /** Callback when question correction is saved (for essay questions) */\n onQuestionCorrectionSubmit?: (\n studentId: string,\n payload: SaveQuestionCorrectionPayload\n ) => Promise<void>;\n /**\n * Callback when a teacher comment is saved on a non-essay question.\n * Omit it to hide the comment field entirely.\n */\n onQuestionCommentSubmit?: (\n studentId: string,\n payload: SaveQuestionCommentPayload\n ) => Promise<void>;\n /** URL of the scanned answer sheet image (for exam mode) */\n answerSheetImageUrl?: string | null;\n /** Callback when \"Ver gabarito escaneado\" button is clicked */\n onViewScannedAnswerSheet?: () => void;\n}\n\n/** Field names for essay correction state updates */\nconst EssayCorrectionField = {\n IsCorrect: 'isCorrect',\n TeacherFeedback: 'teacherFeedback',\n} as const;\n\n/**\n * Modal component for correcting or viewing student activity details\n *\n * Displays student information, statistics cards, observation section,\n * and a list of questions with their status.\n *\n * @param props - Component props\n * @returns JSX element\n *\n * @example\n * ```tsx\n * <CorrectActivityModal\n * isOpen={isOpen}\n * onClose={() => setIsOpen(false)}\n * data={studentData}\n * isViewOnly={false}\n * onObservationSubmit={(obs, files) => console.log(obs, files)}\n * />\n * ```\n */\nconst CorrectActivityModal = ({\n isOpen,\n onClose,\n data,\n isViewOnly = false,\n onObservationSubmit,\n onQuestionCorrectionSubmit,\n onQuestionCommentSubmit,\n answerSheetImageUrl,\n onViewScannedAnswerSheet,\n}: CorrectActivityModalProps) => {\n const [observation, setObservation] = useState('');\n const [isObservationExpanded, setIsObservationExpanded] = useState(false);\n const [isObservationSaved, setIsObservationSaved] = useState(false);\n const [savedObservation, setSavedObservation] = useState('');\n const [attachedFiles, setAttachedFiles] = useState<AttachedFile[]>([]);\n const [savedFiles, setSavedFiles] = useState<AttachedFile[]>([]);\n const [existingAttachment, setExistingAttachment] = useState<string | null>(\n null\n );\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n // Toast store for notifications\n const addToast = useToastStore((state) => state.addToast);\n\n /** Essay grading input (grade + observation), keyed by question number. */\n const [questionCorrections, setQuestionCorrections] = useState<\n Record<\n number,\n {\n isCorrect: boolean | null;\n teacherFeedback: string;\n isSaving: boolean;\n isSaved: boolean;\n }\n >\n >({});\n\n /**\n * Comments persisted during this session, keyed by question number.\n *\n * `data` is a snapshot the caller refetches on its own schedule, so without\n * this the field kept comparing the draft against the comment as it was when\n * the modal opened — and Save stayed enabled on a comment already saved.\n */\n const [savedComments, setSavedComments] = useState<Record<number, string>>(\n {}\n );\n\n /**\n * Reset state when modal opens or student changes\n * Load existing observation and attachment if available\n */\n useEffect(() => {\n if (isOpen) {\n setObservation('');\n setIsObservationExpanded(false);\n setAttachedFiles([]);\n setSavedFiles([]);\n setSavedComments({});\n setExistingAttachment(data?.attachment ?? null);\n\n // Load existing observation/attachment if available\n if (data?.observation || data?.attachment) {\n setIsObservationSaved(true);\n setSavedObservation(data.observation || '');\n } else {\n setIsObservationSaved(false);\n setSavedObservation('');\n }\n\n // Essay grading state only. Comments on objective questions live inside\n // QuestionCommentField, which owns its own draft.\n const initialCorrections: Record<\n number,\n {\n isCorrect: boolean | null;\n teacherFeedback: string;\n isSaving: boolean;\n isSaved: boolean;\n }\n > = {};\n data?.questions?.forEach((questionData) => {\n if (questionData.question.questionType === QUESTION_TYPE.DISSERTATIVA) {\n initialCorrections[questionData.questionNumber] = {\n isCorrect: questionData.correction?.isCorrect ?? null,\n // Opens on the AI's text so the teacher reviews a correction\n // instead of writing one from scratch. The two are separate fields\n // on the answer and stay separate on the server — saving here writes\n // only the teacher's copy, which is what turns the tag into\n // \"IA + professor\" while the original AI text remains readable.\n teacherFeedback: resolveCorrectionText({\n ...questionData.correction,\n }),\n isSaving: false,\n isSaved: questionData.correction?.isCorrect != null,\n };\n }\n });\n setQuestionCorrections(initialCorrections);\n }\n }, [\n isOpen,\n data?.studentId,\n data?.observation,\n data?.attachment,\n data?.questions,\n ]);\n\n /**\n * Handle opening observation section\n */\n const handleOpenObservation = () => {\n setIsObservationExpanded(true);\n };\n\n /**\n * Handle adding files (single file mode - replaces existing file)\n * @param files - Files to add\n */\n const handleFilesAdd = (files: File[]) => {\n const newFile = files[0];\n if (newFile) {\n setAttachedFiles([{ file: newFile, id: generateFileId() }]);\n }\n };\n\n /**\n * Handle removing a file\n * @param id - File ID to remove\n */\n const handleFileRemove = (id: string) => {\n setAttachedFiles((prev) => prev.filter((f) => f.id !== id));\n };\n\n /**\n * Handle saving observation\n */\n const handleSaveObservation = () => {\n if (observation.trim() || attachedFiles.length > 0 || existingAttachment) {\n // Validate studentId before saving to prevent silent no-op\n if (!data?.studentId) {\n return;\n }\n\n setSavedObservation(observation);\n setSavedFiles([...attachedFiles]);\n setIsObservationSaved(true);\n setIsObservationExpanded(false);\n\n // Pass studentId explicitly from data prop to avoid stale closure issues\n onObservationSubmit?.(\n data.studentId,\n observation,\n attachedFiles.map((f) => f.file),\n existingAttachment\n );\n }\n };\n\n /**\n * Handle editing observation\n */\n const handleEditObservation = () => {\n setObservation(savedObservation);\n setAttachedFiles([...savedFiles]);\n setIsObservationSaved(false);\n setIsObservationExpanded(true);\n };\n\n /**\n * Handle saving essay question correction\n */\n const handleSaveEssayCorrection = useCallback(\n async (questionNumber: number) => {\n if (!data?.studentId || !onQuestionCorrectionSubmit) return;\n\n const correction = questionCorrections[questionNumber];\n if (correction?.isCorrect == null) {\n return;\n }\n\n setQuestionCorrections((prev) => ({\n ...prev,\n [questionNumber]: { ...prev[questionNumber], isSaving: true },\n }));\n\n try {\n // Find the question data to get questionId\n const questionData = data?.questions.find(\n (q) => q.questionNumber === questionNumber\n );\n if (!questionData) {\n console.error('Questão não encontrada:', questionNumber);\n return;\n }\n\n await onQuestionCorrectionSubmit(data.studentId, {\n questionId: questionData.question.id,\n isCorrect: correction.isCorrect,\n teacherFeedback: correction.teacherFeedback,\n });\n\n // Mark as saved and show success toast\n setQuestionCorrections((prev) => ({\n ...prev,\n [questionNumber]: {\n ...prev[questionNumber],\n isSaving: false,\n isSaved: true,\n },\n }));\n\n addToast({\n title: 'Correção salva',\n description: `A correção da questão ${questionNumber} foi salva com sucesso.`,\n variant: 'solid',\n action: 'success',\n position: 'top-right',\n });\n } catch (error) {\n console.error('Erro ao salvar correção da questão:', error);\n\n setQuestionCorrections((prev) => ({\n ...prev,\n [questionNumber]: { ...prev[questionNumber], isSaving: false },\n }));\n\n addToast({\n title: 'Erro ao salvar correção',\n description: 'Não foi possível salvar a correção. Tente novamente.',\n variant: 'solid',\n action: 'warning',\n position: 'top-right',\n });\n }\n },\n [\n data?.studentId,\n data?.questions,\n questionCorrections,\n onQuestionCorrectionSubmit,\n addToast,\n ]\n );\n\n /**\n * Handle saving a teacher comment on a non-essay question\n *\n * Objective questions are graded automatically, so this saves the comment\n * alone — it never touches the question's status badge.\n */\n const handleSaveQuestionComment = useCallback(\n async (questionNumber: number, comment: string) => {\n if (!data?.studentId || !onQuestionCommentSubmit) return;\n\n const questionData = data.questions.find(\n (q) => q.questionNumber === questionNumber\n );\n if (!questionData) return;\n\n try {\n await onQuestionCommentSubmit(data.studentId, {\n questionId: questionData.question.id,\n teacherFeedback: comment,\n });\n\n setSavedComments((prev) => ({ ...prev, [questionNumber]: comment }));\n\n addToast({\n title: 'Comentário salvo',\n description: `O comentário da questão ${questionNumber} foi salvo com sucesso.`,\n variant: 'solid',\n action: 'success',\n position: 'top-right',\n });\n } catch (error) {\n console.error('Erro ao salvar comentário da questão:', error);\n\n addToast({\n title: 'Erro ao salvar comentário',\n description: 'Não foi possível salvar o comentário. Tente novamente.',\n variant: 'solid',\n action: 'warning',\n position: 'top-right',\n });\n // Rethrow so the field keeps the draft and shows its inline error.\n throw error;\n }\n },\n [data?.studentId, data?.questions, onQuestionCommentSubmit, addToast]\n );\n\n /**\n * Update essay correction state\n * Resets isSaved to false when isCorrect changes so badge only updates after saving\n */\n const updateEssayCorrection = useCallback(\n (\n questionNumber: number,\n field:\n | (typeof EssayCorrectionField)['IsCorrect']\n | (typeof EssayCorrectionField)['TeacherFeedback'],\n value: boolean | string\n ) => {\n setQuestionCorrections((prev) => ({\n ...prev,\n [questionNumber]: {\n ...prev[questionNumber],\n [field]: value,\n // Reset isSaved when isCorrect changes so badge doesn't update until saved\n ...(field === EssayCorrectionField.IsCorrect\n ? { isSaved: false }\n : {}),\n },\n }));\n },\n []\n );\n\n /**\n * Get accordion title based on question type\n */\n const getAccordionTitle = (questionType: QUESTION_TYPE): string => {\n switch (questionType) {\n case QUESTION_TYPE.ALTERNATIVA:\n case QUESTION_TYPE.MULTIPLA_ESCOLHA:\n case QUESTION_TYPE.VERDADEIRO_FALSO:\n return 'Alternativas';\n case QUESTION_TYPE.DISSERTATIVA:\n return 'Resposta';\n case QUESTION_TYPE.PREENCHER_LACUNAS:\n return 'Preencher Lacunas';\n case QUESTION_TYPE.IMAGEM:\n return 'Imagem';\n case QUESTION_TYPE.RELACIONAR:\n return 'Ligar Pontos';\n default:\n return 'Resposta';\n }\n };\n\n /**\n * Render question content using Quiz format renderers with accordion\n */\n const renderQuestionContent = (\n questionData: StudentActivityCorrectionData['questions'][number]\n ) => {\n const { question, result } = questionData;\n const questionType = question.questionType;\n const accordionTitle = getAccordionTitle(questionType);\n\n let content: ReactNode;\n\n switch (questionType) {\n case QUESTION_TYPE.ALTERNATIVA:\n content = renderQuestionAlternative({\n question,\n result,\n });\n break;\n case QUESTION_TYPE.MULTIPLA_ESCOLHA:\n content = renderQuestionMultipleChoice({\n question,\n result,\n });\n break;\n case QUESTION_TYPE.VERDADEIRO_FALSO:\n content = renderQuestionTrueOrFalse({\n question,\n result,\n });\n break;\n case QUESTION_TYPE.DISSERTATIVA:\n content = (\n <>\n {renderQuestionDissertative({\n result,\n })}\n {onQuestionCorrectionSubmit && (\n <div className=\"space-y-4 border-t border-border-100 pt-4 mt-4\">\n {renderEssayCorrectionFields(questionData)}\n </div>\n )}\n </>\n );\n break;\n case QUESTION_TYPE.PREENCHER_LACUNAS:\n content = renderQuestionFill({\n question,\n result,\n });\n break;\n case QUESTION_TYPE.IMAGEM:\n content = renderQuestionImage({\n question,\n result,\n });\n break;\n case QUESTION_TYPE.RELACIONAR:\n content = renderQuestionConnectDots({\n question,\n result,\n });\n break;\n default:\n // Fallback: try to render based on options presence\n if (question.options && question.options.length > 0) {\n content = renderQuestionAlternative({\n question,\n result,\n });\n } else {\n content = renderQuestionDissertative({\n result,\n });\n }\n }\n\n // Essay questions already got their field inside the DISSERTATIVA case,\n // bundled with the grade. Every other type gets the comment on its own.\n const showCommentField =\n questionType !== QUESTION_TYPE.DISSERTATIVA && !!onQuestionCommentSubmit;\n\n const savedComment =\n savedComments[questionData.questionNumber] ??\n questionData.correction?.teacherFeedback ??\n '';\n\n // A view-only caller (the exam details screen passes neither callback) gets\n // no editor at all, and the essay renderer no longer echoes the comment on\n // its own. Without this the teacher's note would simply disappear there.\n const hasEditor =\n questionType === QUESTION_TYPE.DISSERTATIVA\n ? !!onQuestionCorrectionSubmit\n : showCommentField;\n const showReadOnlyComment = !hasEditor && savedComment !== '';\n\n return (\n <CardAccordation\n value={`accordion-${questionData.questionNumber}`}\n className=\"border border-border-100 rounded-lg\"\n trigger={\n <div className=\"py-3 pr-2 w-full\">\n <Text className=\"text-sm font-bold text-text-950\">\n {accordionTitle}\n </Text>\n </div>\n }\n >\n {content}\n {showCommentField && (\n <div className=\"border-t border-border-100 pt-4 mt-4\">\n {/* Keyed by the record it edits. The field deliberately keeps a\n dirty draft through a `value` change so an in-flight save cannot\n discard it — but a draft written for one student must never\n survive into another, and remounting resets it for free. */}\n <QuestionCommentField\n key={`${data?.studentId}-${questionData.question.id}`}\n value={savedComment}\n onSave={(comment) =>\n handleSaveQuestionComment(questionData.questionNumber, comment)\n }\n />\n </div>\n )}\n {showReadOnlyComment && (\n <div className=\"border-t border-border-100 pt-4 mt-4 space-y-2\">\n <Text size=\"xs\" weight=\"normal\" color=\"text-text-500\">\n Comentário do professor:\n </Text>\n <div className=\"p-3 bg-background-50 rounded-lg border border-border-100\">\n <HtmlMathRenderer\n content={savedComment}\n className=\"text-sm text-text-700\"\n />\n </div>\n </div>\n )}\n </CardAccordation>\n );\n };\n\n /**\n * Render essay correction fields (radio group, textarea, save button)\n */\n const renderEssayCorrectionFields = (\n questionData: StudentActivityCorrectionData['questions'][number]\n ) => {\n const correction = questionCorrections[questionData.questionNumber] || {\n isCorrect: null,\n teacherFeedback: '',\n isSaving: false,\n };\n\n let radioValue;\n if (correction.isCorrect === null) {\n radioValue = undefined;\n } else if (correction.isCorrect) {\n radioValue = 'true';\n } else {\n radioValue = 'false';\n }\n\n const aiCorrectionStatus = questionData.correction?.aiCorrectionStatus;\n const correctionSource = questionData.correction?.correctionSource;\n const isAiCorrecting = aiCorrectionStatus === AI_CORRECTION_STATUS.PENDING;\n\n return (\n <>\n {/* Who corrected this answer, and whether the AI is still working on it */}\n {(correctionSource || isAiCorrecting) && (\n <div className=\"flex flex-wrap items-center gap-2\">\n <CorrectionSourceTag\n correctionSource={correctionSource}\n aiCorrectionStatus={aiCorrectionStatus}\n />\n {correctionSource === CORRECTION_SOURCE.IA && (\n // The teacher is about to overwrite a correction the student may\n // already have read. Saying so is the difference between reviewing\n // and silently replacing.\n <Text className=\"text-xs text-text-600\">\n Revise o veredito e a observação abaixo — salvar marca esta\n correção como revisada por você.\n </Text>\n )}\n {isAiCorrecting && (\n <Text className=\"text-xs text-text-600\">\n A IA ainda está corrigindo. Corrigir agora substitui o resultado\n dela.\n </Text>\n )}\n </div>\n )}\n\n {/* Is correct radio group */}\n <div className=\"space-y-2\">\n <Text className=\"text-sm font-semibold text-text-950\">\n Resposta está correta?\n </Text>\n <div className=\"flex gap-4\">\n <Radio\n name={`isCorrect-${questionData.questionNumber}`}\n value=\"true\"\n id={`correct-yes-${questionData.questionNumber}`}\n label=\"Sim\"\n size=\"medium\"\n checked={radioValue === 'true'}\n onChange={(e) => {\n if (e.target.checked) {\n updateEssayCorrection(\n questionData.questionNumber,\n EssayCorrectionField.IsCorrect,\n true\n );\n }\n }}\n />\n <Radio\n name={`isCorrect-${questionData.questionNumber}`}\n value=\"false\"\n id={`correct-no-${questionData.questionNumber}`}\n label=\"Não\"\n size=\"medium\"\n checked={radioValue === 'false'}\n onChange={(e) => {\n if (e.target.checked) {\n updateEssayCorrection(\n questionData.questionNumber,\n EssayCorrectionField.IsCorrect,\n false\n );\n }\n }}\n />\n </div>\n </div>\n\n {/* Teacher feedback textarea */}\n <div className=\"space-y-2\">\n <Text className=\"text-sm font-semibold text-text-950\">\n Incluir observação\n </Text>\n <TextArea\n value={correction.teacherFeedback}\n onChange={(e) => {\n updateEssayCorrection(\n questionData.questionNumber,\n EssayCorrectionField.TeacherFeedback,\n e.target.value\n );\n }}\n placeholder=\"Escreva uma observação sobre a resposta do aluno\"\n rows={3}\n size=\"medium\"\n />\n </div>\n\n {/* Save button — one save for the grade and the observation together */}\n <div className=\"flex justify-end\">\n <Button\n variant=\"outline\"\n size=\"medium\"\n onClick={() =>\n handleSaveEssayCorrection(questionData.questionNumber)\n }\n disabled={\n correction.isCorrect === null ||\n correction.isSaving ||\n !onQuestionCorrectionSubmit\n }\n >\n {correction.isSaving ? 'Salvando...' : 'Salvar'}\n </Button>\n </div>\n </>\n );\n };\n\n if (!data) return null;\n\n const title = isViewOnly ? 'Detalhes da atividade' : 'Corrigir atividade';\n const formattedScore = data.score == null ? '-' : data.score.toFixed(1);\n\n /**\n * Render observation section based on current state\n * Allows observations in all activity statuses (not just pending correction)\n * @returns JSX element for observation section\n */\n const renderObservationSection = () => {\n /**\n * Get file name from URL\n * @param url - File URL\n * @returns File name extracted from URL\n */\n const getFileNameFromUrl = (_url: string): string => 'Anexado';\n\n /**\n * Render attachment input section for expanded state\n * @returns JSX element for attachment input\n */\n const renderAttachmentInput = () => {\n if (attachedFiles.length > 0) {\n return (\n <div className=\"flex items-center justify-center gap-2 px-5 h-10 bg-secondary-500 rounded-full min-w-0 max-w-[150px]\">\n <PaperclipIcon size={18} className=\"text-text-800 flex-shrink-0\" />\n <span className=\"text-base font-medium text-text-800 truncate\">\n {attachedFiles[0].file.name}\n </span>\n <button\n type=\"button\"\n onClick={() => handleFileRemove(attachedFiles[0].id)}\n className=\"text-text-700 hover:text-text-800 flex-shrink-0\"\n aria-label={`Remover ${attachedFiles[0].file.name}`}\n >\n <XIcon size={18} />\n </button>\n </div>\n );\n }\n\n if (existingAttachment) {\n return (\n <div className=\"flex items-center gap-2\">\n <a\n href={existingAttachment}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n 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\"\n >\n <PaperclipIcon\n size={18}\n className=\"text-text-800 flex-shrink-0\"\n />\n <span className=\"text-base font-medium text-text-800 truncate\">\n {getFileNameFromUrl(existingAttachment)}\n </span>\n </a>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"small\"\n onClick={() => fileInputRef.current?.click()}\n className=\"flex items-center gap-2\"\n >\n <PaperclipIcon size={18} />\n Trocar\n </Button>\n </div>\n );\n }\n\n return (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"small\"\n onClick={() => fileInputRef.current?.click()}\n className=\"flex items-center gap-2\"\n >\n <PaperclipIcon size={18} />\n Anexar\n </Button>\n );\n };\n\n // State: Saved\n if (isObservationSaved) {\n return (\n <div className=\"bg-background border border-border-100 rounded-lg p-4 space-y-2\">\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3\">\n <Text className=\"text-sm font-bold text-text-950\">Observação</Text>\n <div className=\"flex items-center gap-3\">\n {/* Show newly attached file */}\n {savedFiles.length > 0 && (\n <div className=\"flex items-center gap-2 px-5 h-10 bg-secondary-500 rounded-full min-w-0 max-w-[150px]\">\n <PaperclipIcon\n size={18}\n className=\"text-text-800 flex-shrink-0\"\n />\n <span className=\"text-base font-medium text-text-800 truncate\">\n {savedFiles[0].file.name}\n </span>\n </div>\n )}\n {/* Show existing attachment from server (URL) */}\n {savedFiles.length === 0 && existingAttachment && (\n <a\n href={existingAttachment}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n 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\"\n >\n <PaperclipIcon\n size={18}\n className=\"text-text-800 flex-shrink-0\"\n />\n <span className=\"text-base font-medium text-text-800 truncate\">\n {getFileNameFromUrl(existingAttachment)}\n </span>\n </a>\n )}\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"small\"\n onClick={handleEditObservation}\n className=\"flex items-center gap-2 flex-shrink-0\"\n >\n <PencilSimpleIcon size={16} />\n Editar\n </Button>\n </div>\n </div>\n {savedObservation && (\n <div className=\"p-3 bg-background-50 rounded-lg\">\n <Text className=\"text-sm text-text-700\">{savedObservation}</Text>\n </div>\n )}\n </div>\n );\n }\n\n // State: Expanded\n if (isObservationExpanded) {\n return (\n <div className=\"bg-background border border-border-100 rounded-lg p-4 space-y-3\">\n <Text className=\"text-sm font-bold text-text-950\">Observação</Text>\n <textarea\n value={observation}\n onChange={(e) => setObservation(e.target.value)}\n placeholder=\"Escreva uma observação para o estudante\"\n className=\"w-full min-h-[80px] p-3 border border-border-100 rounded-lg text-sm text-text-700 placeholder:text-text-400 resize-none focus:outline-none focus:ring-2 focus:ring-primary-500\"\n />\n {/* Hidden file input */}\n <input\n type=\"file\"\n ref={fileInputRef}\n className=\"hidden\"\n onChange={(e) => {\n const selectedFiles = e.target.files;\n if (selectedFiles && selectedFiles.length > 0) {\n handleFilesAdd(Array.from(selectedFiles));\n }\n if (fileInputRef.current) {\n fileInputRef.current.value = '';\n }\n }}\n aria-label=\"Selecionar arquivo\"\n />\n {/* Buttons row: File indicator or Anexar button left, Salvar right */}\n <div className=\"flex flex-col-reverse sm:flex-row gap-3 sm:justify-between\">\n {renderAttachmentInput()}\n <Button\n type=\"button\"\n size=\"small\"\n onClick={handleSaveObservation}\n disabled={\n !observation.trim() &&\n attachedFiles.length === 0 &&\n !existingAttachment\n }\n >\n Salvar\n </Button>\n </div>\n {data.observation && (\n <div className=\"p-3 bg-background-50 rounded-lg\">\n <Text className=\"text-xs text-text-500\">\n Observação anterior:\n </Text>\n <Text className=\"text-sm text-text-700\">{data.observation}</Text>\n </div>\n )}\n </div>\n );\n }\n\n // State: Closed (default)\n return (\n <div className=\"bg-background border border-border-100 rounded-lg p-4 flex flex-col sm:flex-row gap-3 sm:items-center sm:justify-between\">\n <Text className=\"text-sm font-bold text-text-950\">Observação</Text>\n <Button type=\"button\" size=\"small\" onClick={handleOpenObservation}>\n Incluir\n </Button>\n </div>\n );\n };\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={onClose}\n title={title}\n size=\"lg\"\n contentClassName=\"max-h-[80vh] overflow-y-auto\"\n >\n <div className=\"space-y-6\">\n {/* Student Info */}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 bg-primary-100 rounded-full flex items-center justify-center\">\n <Text className=\"text-lg font-semibold text-primary-700\">\n {data.studentName?.charAt(0).toUpperCase() || '-'}\n </Text>\n </div>\n <Text className=\"text-lg font-medium text-text-950\">\n {data.studentName || 'Aluno'}\n </Text>\n </div>\n\n {/* View scanned answer sheet button */}\n {answerSheetImageUrl && onViewScannedAnswerSheet && (\n <Button\n variant=\"outline\"\n size=\"small\"\n onClick={onViewScannedAnswerSheet}\n >\n <ImageIcon size={18} className=\"mr-2\" />\n Ver gabarito escaneado\n </Button>\n )}\n </div>\n\n {/* Stats Cards */}\n <div className=\"grid grid-cols-3 gap-4\">\n <StatCard label=\"Nota\" value={formattedScore} variant=\"score\" />\n <StatCard\n label=\"N° de questões corretas\"\n value={data.correctCount}\n variant=\"correct\"\n />\n <StatCard\n label=\"N° de questões incorretas\"\n value={data.incorrectCount}\n variant=\"incorrect\"\n />\n </div>\n\n {/* Observation Section */}\n {renderObservationSection()}\n\n {/* Questions List */}\n <div className=\"space-y-2\">\n <Text className=\"text-sm font-bold text-text-950\">Respostas</Text>\n {/* A student can reach this modal with nothing answered: an in-person\n exam whose essay arrived before the answer sheet was scanned, or a\n digital activity never handed in. Saying so beats an empty list. */}\n {data.questions?.length === 0 && (\n <Text className=\"text-sm text-text-600\">\n Nenhuma resposta deste estudante foi recebida até agora.\n </Text>\n )}\n <AccordionGroup type=\"multiple\" className=\"space-y-2\">\n {data.questions?.map((questionData) => {\n // Check if we have a local correction for essay questions\n const localCorrection =\n questionCorrections[questionData.questionNumber];\n const isEssayWithLocalCorrection =\n questionData.question.questionType ===\n QUESTION_TYPE.DISSERTATIVA &&\n localCorrection?.isSaved &&\n localCorrection?.isCorrect != null;\n\n // Use local correction status for essay questions, otherwise use original\n let badgeConfig;\n if (isEssayWithLocalCorrection) {\n const localStatus = localCorrection.isCorrect\n ? QUESTION_STATUS.CORRETA\n : QUESTION_STATUS.INCORRETA;\n badgeConfig = getQuestionStatusBadgeConfig(localStatus);\n } else {\n const status = getQuestionStatusFromData(questionData);\n badgeConfig = getQuestionStatusBadgeConfig(status);\n }\n\n return (\n <CardAccordation\n key={questionData.questionNumber}\n value={`question-${questionData.questionNumber}`}\n className=\"bg-background rounded-xl\"\n trigger={\n <div className=\"flex items-center justify-between gap-2 w-full py-3 pr-2\">\n <Text className=\"text-base font-bold text-text-950\">\n Questão {questionData.questionNumber}\n </Text>\n <div className=\"flex items-center gap-2 flex-wrap justify-end\">\n {/* Quem corrigiu, visível com o acordeão fechado: é o\n que permite varrer a lista e achar as questões que a\n IA corrigiu e ninguém revisou ainda, sem abrir uma\n por uma. */}\n <CorrectionSourceTag\n correctionSource={\n questionData.correction?.correctionSource\n }\n aiCorrectionStatus={\n questionData.correction?.aiCorrectionStatus\n }\n />\n <Badge\n className={cn(\n 'text-xs px-2 py-1',\n badgeConfig.bgColor,\n badgeConfig.textColor\n )}\n >\n {badgeConfig.label}\n </Badge>\n </div>\n </div>\n }\n >\n <div className=\"space-y-4 pt-2\">\n {/* Question statement */}\n {questionData.question.statement && (\n <HtmlMathRenderer\n content={questionData.question.statement}\n className=\"text-sm text-text-700\"\n />\n )}\n\n {/* Question content based on type */}\n {renderQuestionContent(questionData)}\n </div>\n </CardAccordation>\n );\n })}\n </AccordionGroup>\n </div>\n </div>\n </Modal>\n );\n};\n\nexport default CorrectActivityModal;\n","import type { ReactNode } from 'react';\nimport { ANSWER_STATUS } from '../../../components/Quiz/useQuizStore';\nimport { AlternativesList } from '../../../components/Alternative/Alternative';\nimport { OptionStatus } from '../../../enums/Options';\nimport Text from '../../../components/Text/Text';\nimport type { QuestionRendererProps } from '../types';\n\n/**\n * Render alternative question (single choice)\n * Returns content without wrapper (for accordion use)\n */\nexport const renderQuestionAlternative = ({\n question,\n result,\n}: QuestionRendererProps): ReactNode => {\n // Check if options have isCorrect defined (can auto-validate even if pending)\n const hasAutoValidation = result?.options?.some(\n (op) => op.isCorrect !== undefined && op.isCorrect !== null\n );\n\n const alternatives = question.options?.map((option) => {\n const isCorrectOption =\n result?.options?.find((op) => op.id === option.id)?.isCorrect || false;\n\n const isSelected =\n result?.selectedOptions?.some(\n (selectedOption) => selectedOption.optionId === option.id\n ) || false;\n\n // Show correct answers if not pending, OR if we can auto-validate (has isCorrect)\n const shouldShowCorrectAnswers =\n result?.answerStatus !== ANSWER_STATUS.PENDENTE_AVALIACAO ||\n hasAutoValidation;\n\n let status: OptionStatus;\n if (shouldShowCorrectAnswers) {\n if (isCorrectOption) {\n status = OptionStatus.CORRECT;\n } else if (isSelected && !isCorrectOption) {\n status = OptionStatus.INCORRECT;\n } else {\n status = OptionStatus.NEUTRAL;\n }\n } else {\n // When pending evaluation and no auto-validation, show all options as neutral\n status = OptionStatus.NEUTRAL;\n }\n\n return {\n label: option.option,\n value: option.id,\n status: status,\n };\n });\n\n if (!alternatives || alternatives.length === 0) {\n return (\n <div>\n <Text size=\"sm\" weight=\"normal\">\n Não há Alternativas\n </Text>\n </div>\n );\n }\n\n return (\n <div className=\"pt-2\">\n <AlternativesList\n mode=\"readonly\"\n key={`question-${question.id}`}\n name={`question-${question.id}`}\n layout=\"compact\"\n alternatives={alternatives}\n selectedValue={result?.selectedOptions?.[0]?.optionId || ''}\n />\n </div>\n );\n};\n","import type { ReactNode } from 'react';\nimport { ANSWER_STATUS } from '../../../components/Quiz/useQuizStore';\nimport { MultipleChoiceList } from '../../../components/MultipleChoice/MultipleChoice';\nimport { OptionStatus } from '../../../enums/Options';\nimport Text from '../../../components/Text/Text';\nimport type { QuestionRendererProps } from '../types';\n\n/**\n * Render multiple choice question\n * Returns content without wrapper (for accordion use)\n */\nexport const renderQuestionMultipleChoice = ({\n question,\n result,\n}: QuestionRendererProps): ReactNode => {\n // Check if options have isCorrect defined (can auto-validate even if pending)\n const hasAutoValidation = result?.options?.some(\n (op) => op.isCorrect !== undefined && op.isCorrect !== null\n );\n\n const choices = question.options?.map((option) => {\n const isCorrectOption =\n result?.options?.find((op) => op.id === option.id)?.isCorrect || false;\n\n const isSelected = result?.selectedOptions?.some(\n (op) => op.optionId === option.id\n );\n\n // Show correct/incorrect status if not pending/blank, OR if we can auto-validate (has isCorrect)\n const shouldShowCorrectAnswers =\n (result?.answerStatus !== ANSWER_STATUS.PENDENTE_AVALIACAO &&\n result?.answerStatus !== ANSWER_STATUS.NAO_RESPONDIDO) ||\n hasAutoValidation;\n\n let status: OptionStatus;\n if (shouldShowCorrectAnswers) {\n if (isCorrectOption) {\n status = OptionStatus.CORRECT;\n } else if (isSelected && !isCorrectOption) {\n status = OptionStatus.INCORRECT;\n } else {\n status = OptionStatus.NEUTRAL;\n }\n } else {\n // When pending evaluation and no auto-validation, show all options as neutral\n status = OptionStatus.NEUTRAL;\n }\n\n return {\n label: option.option,\n value: option.id,\n status,\n };\n });\n\n if (!choices || choices.length === 0) {\n return (\n <div>\n <Text size=\"sm\" weight=\"normal\">\n Não há Caixas de seleção\n </Text>\n </div>\n );\n }\n\n const selectedValues =\n result?.selectedOptions?.map((op) => op.optionId) || [];\n\n return (\n <div className=\"pt-2\">\n <MultipleChoiceList\n mode=\"readonly\"\n key={`question-${question.id}`}\n name={`question-${question.id}`}\n choices={choices}\n selectedValues={selectedValues}\n />\n </div>\n );\n};\n","import type { ReactNode } from 'react';\nimport { ANSWER_STATUS } from '../../../components/Quiz/useQuizStore';\nimport type { QuestionRendererProps } from '../types';\nimport {\n TrueFalseStatementList,\n type TrueFalseStatement,\n} from '../../../components/shared/TrueFalseStatementList';\nimport { TrueFalseEnum } from '../../../enums/Quiz';\n\n/**\n * Render true or false question\n * Each option has a statement, and student marks V or F for each\n * Returns content without wrapper (for accordion use)\n */\nexport const renderQuestionTrueOrFalse = ({\n question,\n result,\n}: QuestionRendererProps): ReactNode => {\n const options = question.options || [];\n\n const shouldShowStatus =\n result?.answerStatus !== ANSWER_STATUS.PENDENTE_AVALIACAO &&\n result?.answerStatus !== ANSWER_STATUS.NAO_RESPONDIDO;\n\n const statements: TrueFalseStatement[] = options.map((option) => {\n // `isCorrect` means two different things in the two arrays: in\n // `selectedOptions` it is what the STUDENT marked (true = V), in `options`\n // it is the answer key (true = the statement is true).\n const studentSelection = result?.selectedOptions?.find(\n (op) => op.optionId === option.id\n );\n const answerKeyOption = result?.options?.find((op) => op.id === option.id);\n\n // `isCorrect` is optional on `selectedOptions`, and an entry that carries no\n // mark means the statement was left blank — not that the student marked F.\n let studentMark: TrueFalseEnum | null = null;\n if (typeof studentSelection?.isCorrect === 'boolean') {\n studentMark = studentSelection.isCorrect\n ? TrueFalseEnum.VERDADEIRO\n : TrueFalseEnum.FALSO;\n }\n\n return {\n id: option.id,\n statement: option.option,\n studentMark,\n isTrue: answerKeyOption?.isCorrect ?? null,\n };\n });\n\n return (\n <TrueFalseStatementList\n statements={statements}\n showCorrectness={shouldShowStatus}\n />\n );\n};\n","import type { ReactNode } from 'react';\nimport Text from '../../../components/Text/Text';\nimport type { QuestionRendererProps } from '../types';\nimport { HtmlMathRenderer } from '../../../components/HtmlMathRenderer';\n\n/**\n * Render essay/dissertative question (readonly mode for correction)\n * Returns content without wrapper (for accordion use)\n */\nexport const renderQuestionDissertative = ({\n result,\n}: Omit<QuestionRendererProps, 'question'>): ReactNode => {\n const localAnswer = result?.answer || '';\n\n return (\n <div className=\"pt-2 space-y-4\">\n <div className=\"space-y-2\">\n <Text size=\"sm\" weight=\"normal\" color=\"text-text-950\">\n Resposta do aluno\n </Text>\n <div className=\"p-3 bg-background-50 rounded-lg border border-border-100\">\n {localAnswer ? (\n <HtmlMathRenderer\n content={localAnswer}\n className=\"text-sm text-text-700\"\n />\n ) : (\n <Text size=\"sm\" weight=\"normal\" color=\"text-text-700\">\n Nenhuma resposta fornecida\n </Text>\n )}\n </div>\n </div>\n\n {/* The teacher's observation is not echoed here. It lives in the editable\n textarea of the correction block right below, already filled in — and\n this read-only copy only appeared when the answer was marked wrong, so\n an observation on a correct answer vanished on reopening the modal. */}\n </div>\n );\n};\n","import type { ReactNode } from 'react';\nimport type { QuestionRendererProps } from '../types';\nimport { FillQuestionContent } from '../components';\n\n/**\n * Render fill in the blanks question\n * Shows text with student answers highlighted (green if correct, red if wrong)\n * Also shows a correct answers section\n * Returns content without wrapper (for accordion use)\n */\nexport const renderQuestionFill = ({\n question,\n result,\n}: QuestionRendererProps): ReactNode => {\n return <FillQuestionContent question={question} result={result} />;\n};\n","import type { ReactNode } from 'react';\nimport type { QuestionRendererProps } from '../types';\nimport {\n ImageAnswerView,\n type ImagePoint,\n} from '../../../components/shared/ImageAnswerView';\nimport {\n DEFAULT_IMAGE_TOLERANCE,\n parseImageCorrectPoint,\n parseImageStudentPoint,\n} from '../../image/imageAnswer.utils';\n\n/**\n * Render image question\n * Shows the question's image, the correct area and the student's click\n */\nexport const renderQuestionImage = ({\n question,\n result,\n}: QuestionRendererProps): ReactNode => {\n const correctPoint: ImagePoint | null = parseImageCorrectPoint(\n question?.options\n );\n const studentPoint: ImagePoint | null = parseImageStudentPoint(result);\n\n return (\n <ImageAnswerView\n imageUrl={question?.additionalContent ?? ''}\n correctPoint={correctPoint}\n studentPoint={studentPoint}\n toleranceRadius={result?.imageTolerance ?? DEFAULT_IMAGE_TOLERANCE}\n />\n );\n};\n","import type { ReactNode } from 'react';\nimport { ANSWER_STATUS } from '../../../components/Quiz/useQuizStore';\nimport type { QuestionRendererProps } from '../types';\nimport { getStatusStyles } from '../../../components/Quiz/QuizContent';\nimport { prependLetterToHtml } from '../../../components/Quiz/Quiz.utils';\nimport { HtmlMathRenderer } from '../../../components/HtmlMathRenderer';\nimport Text from '../../../components/Text/Text';\nimport { OptionStatus } from '../../../enums/Options';\nimport { cn } from '../../../utils/utils';\nimport { stripHtmlTags } from '../../stringUtils';\nimport { getStatusBadge } from '../components';\n\n/** One matching pair, already normalized for rendering. */\ninterface MatchingPair {\n id: string;\n /** Left-hand item; may contain HTML and LaTeX */\n leftItem: string;\n /** What the student matched to it. Null when left blank. */\n studentValue: string | null;\n /** Answer key for this pair. Null when the question carries none. */\n correctValue: string | null;\n}\n\n/**\n * Normalize a value before comparing it, the same way the backend does.\n *\n * `evaluateMatchingAnswers` grades with `trim().toLowerCase()`, so comparing the\n * raw strings here could mark a pair red that the backend counted as correct.\n */\nconst normalizeValue = (value: string): string => value.trim().toLowerCase();\n\n/**\n * Read the student's picks out of the raw `answer` column.\n *\n * Submissions exist in two shapes: the canonical array the backend persists and\n * an older map keyed by option id. `answer` is free text, so a malformed value\n * must degrade to \"nothing answered\" rather than break the correction screen.\n *\n * @param answer - Raw `answer` value from the student's submission\n * @returns Map of option id to the value the student picked\n */\nconst collectFromArray = (entries: unknown[]): Map<string, string> => {\n const picks = new Map<string, string>();\n\n for (const entry of entries) {\n const { optionId, selectedValue } = (entry ?? {}) as {\n optionId?: unknown;\n selectedValue?: unknown;\n };\n if (typeof optionId === 'string' && typeof selectedValue === 'string') {\n picks.set(optionId, selectedValue);\n }\n }\n\n return picks;\n};\n\nconst collectFromMap = (entries: object): Map<string, string> => {\n const picks = new Map<string, string>();\n\n for (const [optionId, selectedValue] of Object.entries(entries)) {\n if (typeof selectedValue === 'string') {\n picks.set(optionId, selectedValue);\n }\n }\n\n return picks;\n};\n\nconst parseLegacyAnswer = (answer: string | null): Map<string, string> => {\n if (!answer) return new Map();\n\n try {\n const parsed: unknown = JSON.parse(answer);\n\n if (Array.isArray(parsed)) return collectFromArray(parsed);\n if (parsed !== null && typeof parsed === 'object')\n return collectFromMap(parsed);\n } catch {\n // Malformed JSON means the answer is unreadable, not that the screen fails.\n }\n\n return new Map();\n};\n\n/**\n * Render a connect-the-dots (RELACIONAR) question.\n *\n * Each option row is one pair: `option` holds the left-hand item and\n * `correctValue` its correct match. The student's picks arrive in\n * `matchingAnswers`, keyed by option id.\n *\n * Replaces a placeholder that rendered the words \"não implementado\" and ignored\n * both `question` and `result`, which left the teacher writing a comment on a\n * question whose answer was never shown.\n *\n * Returns content without wrapper (for accordion use).\n *\n * @param props - Question renderer props\n * @returns JSX element\n */\nexport const renderQuestionConnectDots = ({\n question,\n result,\n}: QuestionRendererProps): ReactNode => {\n const options = question?.options || [];\n\n const picks =\n result?.matchingAnswers && result.matchingAnswers.length > 0\n ? new Map(\n result.matchingAnswers.map((match) => [\n match.optionId,\n match.selectedValue,\n ])\n )\n : parseLegacyAnswer(result?.answer ?? null);\n\n const pairs: MatchingPair[] = options.map((option) => ({\n id: option.id,\n leftItem: option.option,\n studentValue: picks.get(option.id) ?? null,\n correctValue: option.correctValue ?? null,\n }));\n\n const shouldShowStatus =\n result?.answerStatus !== ANSWER_STATUS.PENDENTE_AVALIACAO &&\n result?.answerStatus !== ANSWER_STATUS.NAO_RESPONDIDO;\n\n // 97 = 'a' in ASCII\n const getLetterByIndex = (index: number) => String.fromCodePoint(97 + index);\n\n if (pairs.length === 0) {\n return (\n <div className=\"pt-2\">\n <Text size=\"sm\" className=\"text-text-500 italic\">\n Nenhuma opção de relacionamento disponível\n </Text>\n </div>\n );\n }\n\n return (\n <div className=\"pt-2\">\n <div className=\"flex flex-col gap-3.5\">\n {pairs.map((pair, index) => {\n const hasAnswerKey = pair.correctValue !== null;\n const hasAnswered = pair.studentValue !== null;\n\n const isStudentCorrect =\n hasAnswerKey &&\n hasAnswered &&\n normalizeValue(pair.studentValue as string) ===\n normalizeValue(pair.correctValue as string);\n\n // Correctness is only shown once the answer key is known AND the\n // answer is no longer pending.\n const canShowCorrectness = shouldShowStatus && hasAnswerKey;\n const variantCorrect = isStudentCorrect\n ? OptionStatus.CORRECT\n : OptionStatus.INCORRECT;\n\n return (\n <section\n key={pair.id || `pair-${index}`}\n className=\"flex flex-col gap-2\"\n >\n <div\n className={cn(\n 'grid grid-cols-[1fr_auto] items-center gap-4 p-2 rounded-md border',\n canShowCorrectness && hasAnswered\n ? getStatusStyles(variantCorrect)\n : ''\n )}\n >\n <HtmlMathRenderer\n content={prependLetterToHtml(\n getLetterByIndex(index),\n pair.leftItem\n )}\n className=\"text-text-900 text-sm\"\n />\n\n {canShowCorrectness && hasAnswered && (\n <div className=\"flex-shrink-0\">\n {getStatusBadge(variantCorrect)}\n </div>\n )}\n </div>\n\n {canShowCorrectness && (\n <span className=\"flex flex-row gap-2 items-center flex-wrap\">\n {hasAnswered ? (\n <>\n <Text size=\"2xs\" weight=\"normal\" color=\"text-text-800\">\n Resposta selecionada:{' '}\n {stripHtmlTags(pair.studentValue as string)}\n </Text>\n {!isStudentCorrect && (\n <Text size=\"2xs\" weight=\"normal\" color=\"text-text-800\">\n | Resposta correta:{' '}\n {stripHtmlTags(pair.correctValue as string)}\n </Text>\n )}\n </>\n ) : (\n <Text size=\"2xs\" weight=\"normal\" color=\"text-text-800\">\n Não respondida | Resposta correta:{' '}\n {stripHtmlTags(pair.correctValue as string)}\n </Text>\n )}\n </span>\n )}\n </section>\n );\n })}\n </div>\n </div>\n );\n};\n","import type { ReactNode } from 'react';\nimport { QUESTION_TYPE } from '../../components/Quiz/useQuizStore';\nimport { renderQuestionAlternative } from './alternative';\nimport { renderQuestionMultipleChoice } from './multipleChoice';\nimport { renderQuestionTrueOrFalse } from './trueOrFalse';\nimport { renderQuestionDissertative } from './dissertative';\nimport { renderQuestionFill } from './fill';\nimport { renderQuestionImage } from './image';\nimport { renderQuestionConnectDots } from './connectDots';\nimport type { QuestionRendererProps } from './types';\n\n// Re-export types\nexport type { QuestionRendererProps } from './types';\n\n// Re-export components\nexport {\n getStatusBadge,\n QuestionContainer,\n QuestionSubTitle,\n FillQuestionContent,\n} from './components';\n\n// Re-export individual renderers\nexport { renderQuestionAlternative } from './alternative';\nexport { renderQuestionMultipleChoice } from './multipleChoice';\nexport { renderQuestionTrueOrFalse } from './trueOrFalse';\nexport { renderQuestionDissertative } from './dissertative';\nexport { renderQuestionFill } from './fill';\nexport { renderQuestionImage } from './image';\nexport { renderQuestionConnectDots } from './connectDots';\n\n/**\n * Map question types to their render functions\n */\nconst questionRendererMap = {\n [QUESTION_TYPE.ALTERNATIVA]: renderQuestionAlternative,\n [QUESTION_TYPE.MULTIPLA_ESCOLHA]: renderQuestionMultipleChoice,\n [QUESTION_TYPE.VERDADEIRO_FALSO]: renderQuestionTrueOrFalse,\n [QUESTION_TYPE.DISSERTATIVA]: renderQuestionDissertative,\n [QUESTION_TYPE.PREENCHER_LACUNAS]: renderQuestionFill,\n [QUESTION_TYPE.IMAGEM]: renderQuestionImage,\n [QUESTION_TYPE.RELACIONAR]: renderQuestionConnectDots,\n};\n\n/**\n * Render question based on question type\n * @param props - Question renderer props\n * @returns Rendered question content\n */\nexport const renderQuestion = (props: QuestionRendererProps): ReactNode => {\n const { question } = props;\n const renderer = questionRendererMap[question.questionType];\n\n if (!renderer) {\n // Fallback: try to render based on options presence\n if (question.options && question.options.length > 0) {\n return renderQuestionAlternative(props);\n }\n return renderQuestionDissertative({ result: props.result });\n }\n\n // Handle renderers with different prop requirements\n switch (question.questionType) {\n case QUESTION_TYPE.DISSERTATIVA:\n return renderQuestionDissertative({ result: props.result });\n case QUESTION_TYPE.IMAGEM:\n return renderQuestionImage({\n question: props.question,\n result: props.result,\n });\n case QUESTION_TYPE.ALTERNATIVA:\n case QUESTION_TYPE.MULTIPLA_ESCOLHA:\n case QUESTION_TYPE.VERDADEIRO_FALSO:\n case QUESTION_TYPE.PREENCHER_LACUNAS:\n case QUESTION_TYPE.RELACIONAR:\n return renderer(props);\n default:\n return null;\n }\n};\n\n/**\n * Type for question renderer map\n */\nexport type QuestionRendererMap = Record<QUESTION_TYPE, () => ReactNode>;\n\n/**\n * Render from a map of renderers\n * @param renderers - Map of question type to render function\n * @param questionType - Type of question to render\n * @returns Rendered content or null\n */\nexport const renderFromMap = (\n renderers: QuestionRendererMap,\n questionType?: QUESTION_TYPE\n) => {\n if (!questionType) return null;\n const renderer = renderers[questionType];\n return renderer ? renderer() : null;\n};\n"]}
1
+ {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-Y7ZBWTD7.js","../src/components/CorrectActivityModal/CorrectActivityModal.tsx","../src/utils/questionRenderer/alternative/index.tsx","../src/utils/questionRenderer/multipleChoice/index.tsx","../src/utils/questionRenderer/trueOrFalse/index.tsx","../src/utils/questionRenderer/dissertative/index.tsx","../src/utils/questionRenderer/fill/index.tsx","../src/utils/questionRenderer/image/index.tsx","../src/utils/questionRenderer/connectDots/index.tsx","../src/utils/questionRenderer/index.tsx"],"names":["jsx","jsxs","Fragment"],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-Y7ZBWTD7.js","sourcesContent":[null,"import {\n useState,\n useRef,\n useEffect,\n useCallback,\n type ReactNode,\n} from 'react';\nimport { PencilSimpleIcon } from '@phosphor-icons/react/dist/csr/PencilSimple';\nimport { PaperclipIcon } from '@phosphor-icons/react/dist/csr/Paperclip';\nimport { XIcon } from '@phosphor-icons/react/dist/csr/X';\nimport { ImageIcon } from '@phosphor-icons/react/dist/csr/Image';\nimport Modal from '../Modal/Modal';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport Badge from '../Badge/Badge';\nimport Radio from '../Radio/Radio';\nimport TextArea from '../TextArea/TextArea';\nimport { CardAccordation, AccordionGroup } from '../Accordation';\nimport { generateFileId } from '../FileAttachment/FileAttachment';\nimport type { AttachedFile } from '../FileAttachment/FileAttachment';\nimport { StatCard } from '../shared/StatCard';\nimport { QuestionCommentField } from '../shared/QuestionCommentField';\nimport { cn } from '../../utils/utils';\nimport {\n AI_CORRECTION_STATUS,\n CORRECTION_SOURCE,\n QUESTION_TYPE,\n} from '../Quiz/useQuizStore';\nimport CorrectionSourceTag from '../Quiz/CorrectionSourceTag';\nimport { resolveCorrectionText } from '../Quiz/correction.utils';\nimport {\n type StudentActivityCorrectionData,\n type SaveQuestionCorrectionPayload,\n type SaveQuestionCommentPayload,\n getQuestionStatusBadgeConfig,\n getQuestionStatusFromData,\n QUESTION_STATUS,\n} from '../../utils/studentActivityCorrection';\nimport {\n renderQuestionAlternative,\n renderQuestionMultipleChoice,\n renderQuestionTrueOrFalse,\n renderQuestionDissertative,\n renderQuestionFill,\n renderQuestionImage,\n renderQuestionConnectDots,\n} from '../../utils/questionRenderer/index';\nimport useToastStore from '../Toast/utils/ToastStore';\nimport { HtmlMathRenderer } from '../HtmlMathRenderer';\n\n/**\n * Props for the CorrectActivityModal component\n */\nexport interface CorrectActivityModalProps {\n /** Whether the modal is open */\n isOpen: boolean;\n /** Function to close the modal */\n onClose: () => void;\n /** Student activity correction data */\n data: StudentActivityCorrectionData | null;\n /** Whether the modal is in view-only mode */\n isViewOnly?: boolean;\n /** Callback when observation is submitted with optional files */\n onObservationSubmit?: (\n studentId: string,\n observation: string,\n files: File[],\n existingAttachment: string | null\n ) => void;\n /** Callback when question correction is saved (for essay questions) */\n onQuestionCorrectionSubmit?: (\n studentId: string,\n payload: SaveQuestionCorrectionPayload\n ) => Promise<void>;\n /**\n * Callback when a teacher comment is saved on a non-essay question.\n * Omit it to hide the comment field entirely.\n */\n onQuestionCommentSubmit?: (\n studentId: string,\n payload: SaveQuestionCommentPayload\n ) => Promise<void>;\n /** URL of the scanned answer sheet image (for exam mode) */\n answerSheetImageUrl?: string | null;\n /** Callback when \"Ver gabarito escaneado\" button is clicked */\n onViewScannedAnswerSheet?: () => void;\n}\n\n/** Field names for essay correction state updates */\nconst EssayCorrectionField = {\n IsCorrect: 'isCorrect',\n TeacherFeedback: 'teacherFeedback',\n} as const;\n\n/**\n * Modal component for correcting or viewing student activity details\n *\n * Displays student information, statistics cards, observation section,\n * and a list of questions with their status.\n *\n * @param props - Component props\n * @returns JSX element\n *\n * @example\n * ```tsx\n * <CorrectActivityModal\n * isOpen={isOpen}\n * onClose={() => setIsOpen(false)}\n * data={studentData}\n * isViewOnly={false}\n * onObservationSubmit={(obs, files) => console.log(obs, files)}\n * />\n * ```\n */\nconst CorrectActivityModal = ({\n isOpen,\n onClose,\n data,\n isViewOnly = false,\n onObservationSubmit,\n onQuestionCorrectionSubmit,\n onQuestionCommentSubmit,\n answerSheetImageUrl,\n onViewScannedAnswerSheet,\n}: CorrectActivityModalProps) => {\n const [observation, setObservation] = useState('');\n const [isObservationExpanded, setIsObservationExpanded] = useState(false);\n const [isObservationSaved, setIsObservationSaved] = useState(false);\n const [savedObservation, setSavedObservation] = useState('');\n const [attachedFiles, setAttachedFiles] = useState<AttachedFile[]>([]);\n const [savedFiles, setSavedFiles] = useState<AttachedFile[]>([]);\n const [existingAttachment, setExistingAttachment] = useState<string | null>(\n null\n );\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n // Toast store for notifications\n const addToast = useToastStore((state) => state.addToast);\n\n /** Essay grading input (grade + observation), keyed by question number. */\n const [questionCorrections, setQuestionCorrections] = useState<\n Record<\n number,\n {\n isCorrect: boolean | null;\n teacherFeedback: string;\n isSaving: boolean;\n isSaved: boolean;\n }\n >\n >({});\n\n /**\n * Comments persisted during this session, keyed by question number.\n *\n * `data` is a snapshot the caller refetches on its own schedule, so without\n * this the field kept comparing the draft against the comment as it was when\n * the modal opened — and Save stayed enabled on a comment already saved.\n */\n const [savedComments, setSavedComments] = useState<Record<number, string>>(\n {}\n );\n\n /**\n * Reset state when modal opens or student changes\n * Load existing observation and attachment if available\n */\n useEffect(() => {\n if (isOpen) {\n setObservation('');\n setIsObservationExpanded(false);\n setAttachedFiles([]);\n setSavedFiles([]);\n setSavedComments({});\n setExistingAttachment(data?.attachment ?? null);\n\n // Load existing observation/attachment if available\n if (data?.observation || data?.attachment) {\n setIsObservationSaved(true);\n setSavedObservation(data.observation || '');\n } else {\n setIsObservationSaved(false);\n setSavedObservation('');\n }\n\n // Essay grading state only. Comments on objective questions live inside\n // QuestionCommentField, which owns its own draft.\n const initialCorrections: Record<\n number,\n {\n isCorrect: boolean | null;\n teacherFeedback: string;\n isSaving: boolean;\n isSaved: boolean;\n }\n > = {};\n data?.questions?.forEach((questionData) => {\n if (questionData.question.questionType === QUESTION_TYPE.DISSERTATIVA) {\n initialCorrections[questionData.questionNumber] = {\n isCorrect: questionData.correction?.isCorrect ?? null,\n // Opens on the AI's text so the teacher reviews a correction\n // instead of writing one from scratch. The two are separate fields\n // on the answer and stay separate on the server — saving here writes\n // only the teacher's copy, which is what turns the tag into\n // \"IA + professor\" while the original AI text remains readable.\n teacherFeedback: resolveCorrectionText({\n ...questionData.correction,\n }),\n isSaving: false,\n isSaved: questionData.correction?.isCorrect != null,\n };\n }\n });\n setQuestionCorrections(initialCorrections);\n }\n }, [\n isOpen,\n data?.studentId,\n data?.observation,\n data?.attachment,\n data?.questions,\n ]);\n\n /**\n * Handle opening observation section\n */\n const handleOpenObservation = () => {\n setIsObservationExpanded(true);\n };\n\n /**\n * Handle adding files (single file mode - replaces existing file)\n * @param files - Files to add\n */\n const handleFilesAdd = (files: File[]) => {\n const newFile = files[0];\n if (newFile) {\n setAttachedFiles([{ file: newFile, id: generateFileId() }]);\n }\n };\n\n /**\n * Handle removing a file\n * @param id - File ID to remove\n */\n const handleFileRemove = (id: string) => {\n setAttachedFiles((prev) => prev.filter((f) => f.id !== id));\n };\n\n /**\n * Handle saving observation\n */\n const handleSaveObservation = () => {\n if (observation.trim() || attachedFiles.length > 0 || existingAttachment) {\n // Validate studentId before saving to prevent silent no-op\n if (!data?.studentId) {\n return;\n }\n\n setSavedObservation(observation);\n setSavedFiles([...attachedFiles]);\n setIsObservationSaved(true);\n setIsObservationExpanded(false);\n\n // Pass studentId explicitly from data prop to avoid stale closure issues\n onObservationSubmit?.(\n data.studentId,\n observation,\n attachedFiles.map((f) => f.file),\n existingAttachment\n );\n }\n };\n\n /**\n * Handle editing observation\n */\n const handleEditObservation = () => {\n setObservation(savedObservation);\n setAttachedFiles([...savedFiles]);\n setIsObservationSaved(false);\n setIsObservationExpanded(true);\n };\n\n /**\n * Handle saving essay question correction\n */\n const handleSaveEssayCorrection = useCallback(\n async (questionNumber: number) => {\n if (!data?.studentId || !onQuestionCorrectionSubmit) return;\n\n const correction = questionCorrections[questionNumber];\n if (correction?.isCorrect == null) {\n return;\n }\n\n setQuestionCorrections((prev) => ({\n ...prev,\n [questionNumber]: { ...prev[questionNumber], isSaving: true },\n }));\n\n try {\n // Find the question data to get questionId\n const questionData = data?.questions.find(\n (q) => q.questionNumber === questionNumber\n );\n if (!questionData) {\n console.error('Questão não encontrada:', questionNumber);\n return;\n }\n\n await onQuestionCorrectionSubmit(data.studentId, {\n questionId: questionData.question.id,\n isCorrect: correction.isCorrect,\n teacherFeedback: correction.teacherFeedback,\n });\n\n // Mark as saved and show success toast\n setQuestionCorrections((prev) => ({\n ...prev,\n [questionNumber]: {\n ...prev[questionNumber],\n isSaving: false,\n isSaved: true,\n },\n }));\n\n addToast({\n title: 'Correção salva',\n description: `A correção da questão ${questionNumber} foi salva com sucesso.`,\n variant: 'solid',\n action: 'success',\n position: 'top-right',\n });\n } catch (error) {\n console.error('Erro ao salvar correção da questão:', error);\n\n setQuestionCorrections((prev) => ({\n ...prev,\n [questionNumber]: { ...prev[questionNumber], isSaving: false },\n }));\n\n addToast({\n title: 'Erro ao salvar correção',\n description: 'Não foi possível salvar a correção. Tente novamente.',\n variant: 'solid',\n action: 'warning',\n position: 'top-right',\n });\n }\n },\n [\n data?.studentId,\n data?.questions,\n questionCorrections,\n onQuestionCorrectionSubmit,\n addToast,\n ]\n );\n\n /**\n * Handle saving a teacher comment on a non-essay question\n *\n * Objective questions are graded automatically, so this saves the comment\n * alone — it never touches the question's status badge.\n */\n const handleSaveQuestionComment = useCallback(\n async (questionNumber: number, comment: string) => {\n if (!data?.studentId || !onQuestionCommentSubmit) return;\n\n const questionData = data.questions.find(\n (q) => q.questionNumber === questionNumber\n );\n if (!questionData) return;\n\n try {\n await onQuestionCommentSubmit(data.studentId, {\n questionId: questionData.question.id,\n teacherFeedback: comment,\n });\n\n setSavedComments((prev) => ({ ...prev, [questionNumber]: comment }));\n\n addToast({\n title: 'Comentário salvo',\n description: `O comentário da questão ${questionNumber} foi salvo com sucesso.`,\n variant: 'solid',\n action: 'success',\n position: 'top-right',\n });\n } catch (error) {\n console.error('Erro ao salvar comentário da questão:', error);\n\n addToast({\n title: 'Erro ao salvar comentário',\n description: 'Não foi possível salvar o comentário. Tente novamente.',\n variant: 'solid',\n action: 'warning',\n position: 'top-right',\n });\n // Rethrow so the field keeps the draft and shows its inline error.\n throw error;\n }\n },\n [data?.studentId, data?.questions, onQuestionCommentSubmit, addToast]\n );\n\n /**\n * Update essay correction state\n * Resets isSaved to false when isCorrect changes so badge only updates after saving\n */\n const updateEssayCorrection = useCallback(\n (\n questionNumber: number,\n field:\n | (typeof EssayCorrectionField)['IsCorrect']\n | (typeof EssayCorrectionField)['TeacherFeedback'],\n value: boolean | string\n ) => {\n setQuestionCorrections((prev) => ({\n ...prev,\n [questionNumber]: {\n ...prev[questionNumber],\n [field]: value,\n // Reset isSaved when isCorrect changes so badge doesn't update until saved\n ...(field === EssayCorrectionField.IsCorrect\n ? { isSaved: false }\n : {}),\n },\n }));\n },\n []\n );\n\n /**\n * Get accordion title based on question type\n */\n const getAccordionTitle = (questionType: QUESTION_TYPE): string => {\n switch (questionType) {\n case QUESTION_TYPE.ALTERNATIVA:\n case QUESTION_TYPE.MULTIPLA_ESCOLHA:\n case QUESTION_TYPE.VERDADEIRO_FALSO:\n return 'Alternativas';\n case QUESTION_TYPE.DISSERTATIVA:\n return 'Resposta';\n case QUESTION_TYPE.PREENCHER_LACUNAS:\n return 'Preencher Lacunas';\n case QUESTION_TYPE.IMAGEM:\n return 'Imagem';\n case QUESTION_TYPE.RELACIONAR:\n return 'Ligar Pontos';\n default:\n return 'Resposta';\n }\n };\n\n /**\n * Render question content using Quiz format renderers with accordion\n */\n const renderQuestionContent = (\n questionData: StudentActivityCorrectionData['questions'][number]\n ) => {\n const { question, result } = questionData;\n const questionType = question.questionType;\n const accordionTitle = getAccordionTitle(questionType);\n\n let content: ReactNode;\n\n switch (questionType) {\n case QUESTION_TYPE.ALTERNATIVA:\n content = renderQuestionAlternative({\n question,\n result,\n });\n break;\n case QUESTION_TYPE.MULTIPLA_ESCOLHA:\n content = renderQuestionMultipleChoice({\n question,\n result,\n });\n break;\n case QUESTION_TYPE.VERDADEIRO_FALSO:\n content = renderQuestionTrueOrFalse({\n question,\n result,\n });\n break;\n case QUESTION_TYPE.DISSERTATIVA:\n content = (\n <>\n {renderQuestionDissertative({\n result,\n })}\n {onQuestionCorrectionSubmit && (\n <div className=\"space-y-4 border-t border-border-100 pt-4 mt-4\">\n {renderEssayCorrectionFields(questionData)}\n </div>\n )}\n </>\n );\n break;\n case QUESTION_TYPE.PREENCHER_LACUNAS:\n content = renderQuestionFill({\n question,\n result,\n });\n break;\n case QUESTION_TYPE.IMAGEM:\n content = renderQuestionImage({\n question,\n result,\n });\n break;\n case QUESTION_TYPE.RELACIONAR:\n content = renderQuestionConnectDots({\n question,\n result,\n });\n break;\n default:\n // Fallback: try to render based on options presence\n if (question.options && question.options.length > 0) {\n content = renderQuestionAlternative({\n question,\n result,\n });\n } else {\n content = renderQuestionDissertative({\n result,\n });\n }\n }\n\n // Essay questions already got their field inside the DISSERTATIVA case,\n // bundled with the grade. Every other type gets the comment on its own.\n const showCommentField =\n questionType !== QUESTION_TYPE.DISSERTATIVA && !!onQuestionCommentSubmit;\n\n const savedComment =\n savedComments[questionData.questionNumber] ??\n questionData.correction?.teacherFeedback ??\n '';\n\n // A view-only caller (the exam details screen passes neither callback) gets\n // no editor at all, and the essay renderer no longer echoes the comment on\n // its own. Without this the teacher's note would simply disappear there.\n const hasEditor =\n questionType === QUESTION_TYPE.DISSERTATIVA\n ? !!onQuestionCorrectionSubmit\n : showCommentField;\n const showReadOnlyComment = !hasEditor && savedComment !== '';\n\n return (\n <CardAccordation\n value={`accordion-${questionData.questionNumber}`}\n className=\"border border-border-100 rounded-lg\"\n trigger={\n <div className=\"py-3 pr-2 w-full\">\n <Text className=\"text-sm font-bold text-text-950\">\n {accordionTitle}\n </Text>\n </div>\n }\n >\n {content}\n {showCommentField && (\n <div className=\"border-t border-border-100 pt-4 mt-4\">\n {/* Keyed by the record it edits. The field deliberately keeps a\n dirty draft through a `value` change so an in-flight save cannot\n discard it — but a draft written for one student must never\n survive into another, and remounting resets it for free. */}\n <QuestionCommentField\n key={`${data?.studentId}-${questionData.question.id}`}\n value={savedComment}\n onSave={(comment) =>\n handleSaveQuestionComment(questionData.questionNumber, comment)\n }\n />\n </div>\n )}\n {showReadOnlyComment && (\n <div className=\"border-t border-border-100 pt-4 mt-4 space-y-2\">\n <Text size=\"xs\" weight=\"normal\" color=\"text-text-500\">\n Comentário do professor:\n </Text>\n <div className=\"p-3 bg-background-50 rounded-lg border border-border-100\">\n <HtmlMathRenderer\n content={savedComment}\n className=\"text-sm text-text-700\"\n />\n </div>\n </div>\n )}\n </CardAccordation>\n );\n };\n\n /**\n * Render essay correction fields (radio group, textarea, save button)\n */\n const renderEssayCorrectionFields = (\n questionData: StudentActivityCorrectionData['questions'][number]\n ) => {\n const correction = questionCorrections[questionData.questionNumber] || {\n isCorrect: null,\n teacherFeedback: '',\n isSaving: false,\n };\n\n let radioValue;\n if (correction.isCorrect === null) {\n radioValue = undefined;\n } else if (correction.isCorrect) {\n radioValue = 'true';\n } else {\n radioValue = 'false';\n }\n\n const aiCorrectionStatus = questionData.correction?.aiCorrectionStatus;\n const correctionSource = questionData.correction?.correctionSource;\n const isAiCorrecting = aiCorrectionStatus === AI_CORRECTION_STATUS.PENDING;\n\n return (\n <>\n {/* Who corrected this answer, and whether the AI is still working on it */}\n {(correctionSource || isAiCorrecting) && (\n <div className=\"flex flex-wrap items-center gap-2\">\n <CorrectionSourceTag\n correctionSource={correctionSource}\n aiCorrectionStatus={aiCorrectionStatus}\n />\n {correctionSource === CORRECTION_SOURCE.IA && (\n // The teacher is about to overwrite a correction the student may\n // already have read. Saying so is the difference between reviewing\n // and silently replacing.\n <Text className=\"text-xs text-text-600\">\n Revise o veredito e a observação abaixo — salvar marca esta\n correção como revisada por você.\n </Text>\n )}\n {isAiCorrecting && (\n <Text className=\"text-xs text-text-600\">\n A IA ainda está corrigindo. Corrigir agora substitui o resultado\n dela.\n </Text>\n )}\n </div>\n )}\n\n {/* Is correct radio group */}\n <div className=\"space-y-2\">\n <Text className=\"text-sm font-semibold text-text-950\">\n Resposta está correta?\n </Text>\n <div className=\"flex gap-4\">\n <Radio\n name={`isCorrect-${questionData.questionNumber}`}\n value=\"true\"\n id={`correct-yes-${questionData.questionNumber}`}\n label=\"Sim\"\n size=\"medium\"\n checked={radioValue === 'true'}\n onChange={(e) => {\n if (e.target.checked) {\n updateEssayCorrection(\n questionData.questionNumber,\n EssayCorrectionField.IsCorrect,\n true\n );\n }\n }}\n />\n <Radio\n name={`isCorrect-${questionData.questionNumber}`}\n value=\"false\"\n id={`correct-no-${questionData.questionNumber}`}\n label=\"Não\"\n size=\"medium\"\n checked={radioValue === 'false'}\n onChange={(e) => {\n if (e.target.checked) {\n updateEssayCorrection(\n questionData.questionNumber,\n EssayCorrectionField.IsCorrect,\n false\n );\n }\n }}\n />\n </div>\n </div>\n\n {/* Teacher feedback textarea */}\n <div className=\"space-y-2\">\n <Text className=\"text-sm font-semibold text-text-950\">\n Incluir observação\n </Text>\n <TextArea\n value={correction.teacherFeedback}\n onChange={(e) => {\n updateEssayCorrection(\n questionData.questionNumber,\n EssayCorrectionField.TeacherFeedback,\n e.target.value\n );\n }}\n placeholder=\"Escreva uma observação sobre a resposta do aluno\"\n rows={3}\n size=\"medium\"\n />\n </div>\n\n {/* Save button — one save for the grade and the observation together */}\n <div className=\"flex justify-end\">\n <Button\n variant=\"outline\"\n size=\"medium\"\n onClick={() =>\n handleSaveEssayCorrection(questionData.questionNumber)\n }\n disabled={\n correction.isCorrect === null ||\n correction.isSaving ||\n !onQuestionCorrectionSubmit\n }\n >\n {correction.isSaving ? 'Salvando...' : 'Salvar'}\n </Button>\n </div>\n </>\n );\n };\n\n if (!data) return null;\n\n const title = isViewOnly ? 'Detalhes da atividade' : 'Corrigir atividade';\n const formattedScore = data.score == null ? '-' : data.score.toFixed(1);\n\n /**\n * Render observation section based on current state\n * Allows observations in all activity statuses (not just pending correction)\n * @returns JSX element for observation section\n */\n const renderObservationSection = () => {\n /**\n * Get file name from URL\n * @param url - File URL\n * @returns File name extracted from URL\n */\n const getFileNameFromUrl = (_url: string): string => 'Anexado';\n\n /**\n * Render attachment input section for expanded state\n * @returns JSX element for attachment input\n */\n const renderAttachmentInput = () => {\n if (attachedFiles.length > 0) {\n return (\n <div className=\"flex items-center justify-center gap-2 px-5 h-10 bg-secondary-500 rounded-full min-w-0 max-w-[150px]\">\n <PaperclipIcon size={18} className=\"text-text-800 flex-shrink-0\" />\n <span className=\"text-base font-medium text-text-800 truncate\">\n {attachedFiles[0].file.name}\n </span>\n <button\n type=\"button\"\n onClick={() => handleFileRemove(attachedFiles[0].id)}\n className=\"text-text-700 hover:text-text-800 flex-shrink-0\"\n aria-label={`Remover ${attachedFiles[0].file.name}`}\n >\n <XIcon size={18} />\n </button>\n </div>\n );\n }\n\n if (existingAttachment) {\n return (\n <div className=\"flex items-center gap-2\">\n <a\n href={existingAttachment}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n 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\"\n >\n <PaperclipIcon\n size={18}\n className=\"text-text-800 flex-shrink-0\"\n />\n <span className=\"text-base font-medium text-text-800 truncate\">\n {getFileNameFromUrl(existingAttachment)}\n </span>\n </a>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"small\"\n onClick={() => fileInputRef.current?.click()}\n className=\"flex items-center gap-2\"\n >\n <PaperclipIcon size={18} />\n Trocar\n </Button>\n </div>\n );\n }\n\n return (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"small\"\n onClick={() => fileInputRef.current?.click()}\n className=\"flex items-center gap-2\"\n >\n <PaperclipIcon size={18} />\n Anexar\n </Button>\n );\n };\n\n // State: Saved\n if (isObservationSaved) {\n return (\n <div className=\"bg-background border border-border-100 rounded-lg p-4 space-y-2\">\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3\">\n <Text className=\"text-sm font-bold text-text-950\">Observação</Text>\n <div className=\"flex items-center gap-3\">\n {/* Show newly attached file */}\n {savedFiles.length > 0 && (\n <div className=\"flex items-center gap-2 px-5 h-10 bg-secondary-500 rounded-full min-w-0 max-w-[150px]\">\n <PaperclipIcon\n size={18}\n className=\"text-text-800 flex-shrink-0\"\n />\n <span className=\"text-base font-medium text-text-800 truncate\">\n {savedFiles[0].file.name}\n </span>\n </div>\n )}\n {/* Show existing attachment from server (URL) */}\n {savedFiles.length === 0 && existingAttachment && (\n <a\n href={existingAttachment}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n 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\"\n >\n <PaperclipIcon\n size={18}\n className=\"text-text-800 flex-shrink-0\"\n />\n <span className=\"text-base font-medium text-text-800 truncate\">\n {getFileNameFromUrl(existingAttachment)}\n </span>\n </a>\n )}\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"small\"\n onClick={handleEditObservation}\n className=\"flex items-center gap-2 flex-shrink-0\"\n >\n <PencilSimpleIcon size={16} />\n Editar\n </Button>\n </div>\n </div>\n {savedObservation && (\n <div className=\"p-3 bg-background-50 rounded-lg\">\n <Text className=\"text-sm text-text-700\">{savedObservation}</Text>\n </div>\n )}\n </div>\n );\n }\n\n // State: Expanded\n if (isObservationExpanded) {\n return (\n <div className=\"bg-background border border-border-100 rounded-lg p-4 space-y-3\">\n <Text className=\"text-sm font-bold text-text-950\">Observação</Text>\n <textarea\n value={observation}\n onChange={(e) => setObservation(e.target.value)}\n placeholder=\"Escreva uma observação para o estudante\"\n className=\"w-full min-h-[80px] p-3 border border-border-100 rounded-lg text-sm text-text-700 placeholder:text-text-400 resize-none focus:outline-none focus:ring-2 focus:ring-primary-500\"\n />\n {/* Hidden file input */}\n <input\n type=\"file\"\n ref={fileInputRef}\n className=\"hidden\"\n onChange={(e) => {\n const selectedFiles = e.target.files;\n if (selectedFiles && selectedFiles.length > 0) {\n handleFilesAdd(Array.from(selectedFiles));\n }\n if (fileInputRef.current) {\n fileInputRef.current.value = '';\n }\n }}\n aria-label=\"Selecionar arquivo\"\n />\n {/* Buttons row: File indicator or Anexar button left, Salvar right */}\n <div className=\"flex flex-col-reverse sm:flex-row gap-3 sm:justify-between\">\n {renderAttachmentInput()}\n <Button\n type=\"button\"\n size=\"small\"\n onClick={handleSaveObservation}\n disabled={\n !observation.trim() &&\n attachedFiles.length === 0 &&\n !existingAttachment\n }\n >\n Salvar\n </Button>\n </div>\n {data.observation && (\n <div className=\"p-3 bg-background-50 rounded-lg\">\n <Text className=\"text-xs text-text-500\">\n Observação anterior:\n </Text>\n <Text className=\"text-sm text-text-700\">{data.observation}</Text>\n </div>\n )}\n </div>\n );\n }\n\n // State: Closed (default)\n return (\n <div className=\"bg-background border border-border-100 rounded-lg p-4 flex flex-col sm:flex-row gap-3 sm:items-center sm:justify-between\">\n <Text className=\"text-sm font-bold text-text-950\">Observação</Text>\n <Button type=\"button\" size=\"small\" onClick={handleOpenObservation}>\n Incluir\n </Button>\n </div>\n );\n };\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={onClose}\n title={title}\n size=\"lg\"\n contentClassName=\"max-h-[80vh] overflow-y-auto\"\n >\n <div className=\"space-y-6\">\n {/* Student Info */}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 bg-primary-100 rounded-full flex items-center justify-center\">\n <Text className=\"text-lg font-semibold text-primary-700\">\n {data.studentName?.charAt(0).toUpperCase() || '-'}\n </Text>\n </div>\n <Text className=\"text-lg font-medium text-text-950\">\n {data.studentName || 'Aluno'}\n </Text>\n </div>\n\n {/* View scanned answer sheet button */}\n {answerSheetImageUrl && onViewScannedAnswerSheet && (\n <Button\n variant=\"outline\"\n size=\"small\"\n onClick={onViewScannedAnswerSheet}\n >\n <ImageIcon size={18} className=\"mr-2\" />\n Ver gabarito escaneado\n </Button>\n )}\n </div>\n\n {/* Stats Cards */}\n <div className=\"grid grid-cols-3 gap-4\">\n <StatCard label=\"Nota\" value={formattedScore} variant=\"score\" />\n <StatCard\n label=\"N° de questões corretas\"\n value={data.correctCount}\n variant=\"correct\"\n />\n <StatCard\n label=\"N° de questões incorretas\"\n value={data.incorrectCount}\n variant=\"incorrect\"\n />\n </div>\n\n {/* Observation Section */}\n {renderObservationSection()}\n\n {/* Questions List */}\n <div className=\"space-y-2\">\n <Text className=\"text-sm font-bold text-text-950\">Respostas</Text>\n {/* A student can reach this modal with nothing answered: an in-person\n exam whose essay arrived before the answer sheet was scanned, or a\n digital activity never handed in. Saying so beats an empty list. */}\n {data.questions?.length === 0 && (\n <Text className=\"text-sm text-text-600\">\n Nenhuma resposta deste estudante foi recebida até agora.\n </Text>\n )}\n <AccordionGroup type=\"multiple\" className=\"space-y-2\">\n {data.questions?.map((questionData) => {\n // Check if we have a local correction for essay questions\n const localCorrection =\n questionCorrections[questionData.questionNumber];\n const isEssayWithLocalCorrection =\n questionData.question.questionType ===\n QUESTION_TYPE.DISSERTATIVA &&\n localCorrection?.isSaved &&\n localCorrection?.isCorrect != null;\n\n // Use local correction status for essay questions, otherwise use original\n let badgeConfig;\n if (isEssayWithLocalCorrection) {\n const localStatus = localCorrection.isCorrect\n ? QUESTION_STATUS.CORRETA\n : QUESTION_STATUS.INCORRETA;\n badgeConfig = getQuestionStatusBadgeConfig(localStatus);\n } else {\n const status = getQuestionStatusFromData(questionData);\n badgeConfig = getQuestionStatusBadgeConfig(status);\n }\n\n return (\n <CardAccordation\n key={questionData.questionNumber}\n value={`question-${questionData.questionNumber}`}\n className=\"bg-background rounded-xl\"\n trigger={\n <div className=\"flex items-center justify-between gap-2 w-full py-3 pr-2\">\n <Text className=\"text-base font-bold text-text-950\">\n Questão {questionData.questionNumber}\n </Text>\n <div className=\"flex items-center gap-2 flex-wrap justify-end\">\n {/* Quem corrigiu, visível com o acordeão fechado: é o\n que permite varrer a lista e achar as questões que a\n IA corrigiu e ninguém revisou ainda, sem abrir uma\n por uma. */}\n <CorrectionSourceTag\n correctionSource={\n questionData.correction?.correctionSource\n }\n aiCorrectionStatus={\n questionData.correction?.aiCorrectionStatus\n }\n />\n <Badge\n className={cn(\n 'text-xs px-2 py-1',\n badgeConfig.bgColor,\n badgeConfig.textColor\n )}\n >\n {badgeConfig.label}\n </Badge>\n </div>\n </div>\n }\n >\n <div className=\"space-y-4 pt-2\">\n {/* Question statement */}\n {questionData.question.statement && (\n <HtmlMathRenderer\n content={questionData.question.statement}\n className=\"text-sm text-text-700\"\n />\n )}\n\n {/* Question content based on type */}\n {renderQuestionContent(questionData)}\n </div>\n </CardAccordation>\n );\n })}\n </AccordionGroup>\n </div>\n </div>\n </Modal>\n );\n};\n\nexport default CorrectActivityModal;\n","import type { ReactNode } from 'react';\nimport { ANSWER_STATUS } from '../../../components/Quiz/useQuizStore';\nimport { AlternativesList } from '../../../components/Alternative/Alternative';\nimport { OptionStatus } from '../../../enums/Options';\nimport Text from '../../../components/Text/Text';\nimport type { QuestionRendererProps } from '../types';\n\n/**\n * Render alternative question (single choice)\n * Returns content without wrapper (for accordion use)\n */\nexport const renderQuestionAlternative = ({\n question,\n result,\n}: QuestionRendererProps): ReactNode => {\n // Check if options have isCorrect defined (can auto-validate even if pending)\n const hasAutoValidation = result?.options?.some(\n (op) => op.isCorrect !== undefined && op.isCorrect !== null\n );\n\n const alternatives = question.options?.map((option) => {\n const isCorrectOption =\n result?.options?.find((op) => op.id === option.id)?.isCorrect || false;\n\n const isSelected =\n result?.selectedOptions?.some(\n (selectedOption) => selectedOption.optionId === option.id\n ) || false;\n\n // Show correct answers if not pending, OR if we can auto-validate (has isCorrect)\n const shouldShowCorrectAnswers =\n result?.answerStatus !== ANSWER_STATUS.PENDENTE_AVALIACAO ||\n hasAutoValidation;\n\n let status: OptionStatus;\n if (shouldShowCorrectAnswers) {\n if (isCorrectOption) {\n status = OptionStatus.CORRECT;\n } else if (isSelected && !isCorrectOption) {\n status = OptionStatus.INCORRECT;\n } else {\n status = OptionStatus.NEUTRAL;\n }\n } else {\n // When pending evaluation and no auto-validation, show all options as neutral\n status = OptionStatus.NEUTRAL;\n }\n\n return {\n label: option.option,\n value: option.id,\n status: status,\n };\n });\n\n if (!alternatives || alternatives.length === 0) {\n return (\n <div>\n <Text size=\"sm\" weight=\"normal\">\n Não há Alternativas\n </Text>\n </div>\n );\n }\n\n return (\n <div className=\"pt-2\">\n <AlternativesList\n mode=\"readonly\"\n key={`question-${question.id}`}\n name={`question-${question.id}`}\n layout=\"compact\"\n alternatives={alternatives}\n selectedValue={result?.selectedOptions?.[0]?.optionId || ''}\n />\n </div>\n );\n};\n","import type { ReactNode } from 'react';\nimport { ANSWER_STATUS } from '../../../components/Quiz/useQuizStore';\nimport { MultipleChoiceList } from '../../../components/MultipleChoice/MultipleChoice';\nimport { OptionStatus } from '../../../enums/Options';\nimport Text from '../../../components/Text/Text';\nimport type { QuestionRendererProps } from '../types';\n\n/**\n * Render multiple choice question\n * Returns content without wrapper (for accordion use)\n */\nexport const renderQuestionMultipleChoice = ({\n question,\n result,\n}: QuestionRendererProps): ReactNode => {\n // Check if options have isCorrect defined (can auto-validate even if pending)\n const hasAutoValidation = result?.options?.some(\n (op) => op.isCorrect !== undefined && op.isCorrect !== null\n );\n\n const choices = question.options?.map((option) => {\n const isCorrectOption =\n result?.options?.find((op) => op.id === option.id)?.isCorrect || false;\n\n const isSelected = result?.selectedOptions?.some(\n (op) => op.optionId === option.id\n );\n\n // Show correct/incorrect status if not pending/blank, OR if we can auto-validate (has isCorrect)\n const shouldShowCorrectAnswers =\n (result?.answerStatus !== ANSWER_STATUS.PENDENTE_AVALIACAO &&\n result?.answerStatus !== ANSWER_STATUS.NAO_RESPONDIDO) ||\n hasAutoValidation;\n\n let status: OptionStatus;\n if (shouldShowCorrectAnswers) {\n if (isCorrectOption) {\n status = OptionStatus.CORRECT;\n } else if (isSelected && !isCorrectOption) {\n status = OptionStatus.INCORRECT;\n } else {\n status = OptionStatus.NEUTRAL;\n }\n } else {\n // When pending evaluation and no auto-validation, show all options as neutral\n status = OptionStatus.NEUTRAL;\n }\n\n return {\n label: option.option,\n value: option.id,\n status,\n };\n });\n\n if (!choices || choices.length === 0) {\n return (\n <div>\n <Text size=\"sm\" weight=\"normal\">\n Não há Caixas de seleção\n </Text>\n </div>\n );\n }\n\n const selectedValues =\n result?.selectedOptions?.map((op) => op.optionId) || [];\n\n return (\n <div className=\"pt-2\">\n <MultipleChoiceList\n mode=\"readonly\"\n key={`question-${question.id}`}\n name={`question-${question.id}`}\n choices={choices}\n selectedValues={selectedValues}\n />\n </div>\n );\n};\n","import type { ReactNode } from 'react';\nimport { ANSWER_STATUS } from '../../../components/Quiz/useQuizStore';\nimport type { QuestionRendererProps } from '../types';\nimport {\n TrueFalseStatementList,\n type TrueFalseStatement,\n} from '../../../components/shared/TrueFalseStatementList';\nimport { TrueFalseEnum } from '../../../enums/Quiz';\n\n/**\n * Render true or false question\n * Each option has a statement, and student marks V or F for each\n * Returns content without wrapper (for accordion use)\n */\nexport const renderQuestionTrueOrFalse = ({\n question,\n result,\n}: QuestionRendererProps): ReactNode => {\n const options = question.options || [];\n\n const shouldShowStatus =\n result?.answerStatus !== ANSWER_STATUS.PENDENTE_AVALIACAO &&\n result?.answerStatus !== ANSWER_STATUS.NAO_RESPONDIDO;\n\n const statements: TrueFalseStatement[] = options.map((option) => {\n // `isCorrect` means two different things in the two arrays: in\n // `selectedOptions` it is what the STUDENT marked (true = V), in `options`\n // it is the answer key (true = the statement is true).\n const studentSelection = result?.selectedOptions?.find(\n (op) => op.optionId === option.id\n );\n const answerKeyOption = result?.options?.find((op) => op.id === option.id);\n\n // `isCorrect` is optional on `selectedOptions`, and an entry that carries no\n // mark means the statement was left blank — not that the student marked F.\n let studentMark: TrueFalseEnum | null = null;\n if (typeof studentSelection?.isCorrect === 'boolean') {\n studentMark = studentSelection.isCorrect\n ? TrueFalseEnum.VERDADEIRO\n : TrueFalseEnum.FALSO;\n }\n\n return {\n id: option.id,\n statement: option.option,\n studentMark,\n isTrue: answerKeyOption?.isCorrect ?? null,\n };\n });\n\n return (\n <TrueFalseStatementList\n statements={statements}\n showCorrectness={shouldShowStatus}\n />\n );\n};\n","import type { ReactNode } from 'react';\nimport Text from '../../../components/Text/Text';\nimport type { QuestionRendererProps } from '../types';\nimport { HtmlMathRenderer } from '../../../components/HtmlMathRenderer';\n\n/**\n * Render essay/dissertative question (readonly mode for correction)\n * Returns content without wrapper (for accordion use)\n */\nexport const renderQuestionDissertative = ({\n result,\n}: Omit<QuestionRendererProps, 'question'>): ReactNode => {\n const localAnswer = result?.answer || '';\n\n return (\n <div className=\"pt-2 space-y-4\">\n <div className=\"space-y-2\">\n <Text size=\"sm\" weight=\"normal\" color=\"text-text-950\">\n Resposta do aluno\n </Text>\n <div className=\"p-3 bg-background-50 rounded-lg border border-border-100\">\n {localAnswer ? (\n <HtmlMathRenderer\n content={localAnswer}\n className=\"text-sm text-text-700\"\n />\n ) : (\n <Text size=\"sm\" weight=\"normal\" color=\"text-text-700\">\n Nenhuma resposta fornecida\n </Text>\n )}\n </div>\n </div>\n\n {/* The teacher's observation is not echoed here. It lives in the editable\n textarea of the correction block right below, already filled in — and\n this read-only copy only appeared when the answer was marked wrong, so\n an observation on a correct answer vanished on reopening the modal. */}\n </div>\n );\n};\n","import type { ReactNode } from 'react';\nimport type { QuestionRendererProps } from '../types';\nimport { FillQuestionContent } from '../components';\n\n/**\n * Render fill in the blanks question\n * Shows text with student answers highlighted (green if correct, red if wrong)\n * Also shows a correct answers section\n * Returns content without wrapper (for accordion use)\n */\nexport const renderQuestionFill = ({\n question,\n result,\n}: QuestionRendererProps): ReactNode => {\n return <FillQuestionContent question={question} result={result} />;\n};\n","import type { ReactNode } from 'react';\nimport type { QuestionRendererProps } from '../types';\nimport {\n ImageAnswerView,\n type ImagePoint,\n} from '../../../components/shared/ImageAnswerView';\nimport {\n DEFAULT_IMAGE_TOLERANCE,\n parseImageCorrectPoint,\n parseImageStudentPoint,\n} from '../../image/imageAnswer.utils';\n\n/**\n * Render image question\n * Shows the question's image, the correct area and the student's click\n */\nexport const renderQuestionImage = ({\n question,\n result,\n}: QuestionRendererProps): ReactNode => {\n const correctPoint: ImagePoint | null = parseImageCorrectPoint(\n question?.options\n );\n const studentPoint: ImagePoint | null = parseImageStudentPoint(result);\n\n return (\n <ImageAnswerView\n imageUrl={question?.additionalContent ?? ''}\n correctPoint={correctPoint}\n studentPoint={studentPoint}\n toleranceRadius={result?.imageTolerance ?? DEFAULT_IMAGE_TOLERANCE}\n />\n );\n};\n","import type { ReactNode } from 'react';\nimport { ANSWER_STATUS } from '../../../components/Quiz/useQuizStore';\nimport type { QuestionRendererProps } from '../types';\nimport { getStatusStyles } from '../../../components/Quiz/QuizContent';\nimport { prependLetterToHtml } from '../../../components/Quiz/Quiz.utils';\nimport { HtmlMathRenderer } from '../../../components/HtmlMathRenderer';\nimport Text from '../../../components/Text/Text';\nimport { OptionStatus } from '../../../enums/Options';\nimport { cn } from '../../../utils/utils';\nimport { stripHtmlTags } from '../../stringUtils';\nimport { getStatusBadge } from '../components';\n\n/** One matching pair, already normalized for rendering. */\ninterface MatchingPair {\n id: string;\n /** Left-hand item; may contain HTML and LaTeX */\n leftItem: string;\n /** What the student matched to it. Null when left blank. */\n studentValue: string | null;\n /** Answer key for this pair. Null when the question carries none. */\n correctValue: string | null;\n}\n\n/**\n * Normalize a value before comparing it, the same way the backend does.\n *\n * `evaluateMatchingAnswers` grades with `trim().toLowerCase()`, so comparing the\n * raw strings here could mark a pair red that the backend counted as correct.\n */\nconst normalizeValue = (value: string): string => value.trim().toLowerCase();\n\n/**\n * Read the student's picks out of the raw `answer` column.\n *\n * Submissions exist in two shapes: the canonical array the backend persists and\n * an older map keyed by option id. `answer` is free text, so a malformed value\n * must degrade to \"nothing answered\" rather than break the correction screen.\n *\n * @param answer - Raw `answer` value from the student's submission\n * @returns Map of option id to the value the student picked\n */\nconst collectFromArray = (entries: unknown[]): Map<string, string> => {\n const picks = new Map<string, string>();\n\n for (const entry of entries) {\n const { optionId, selectedValue } = (entry ?? {}) as {\n optionId?: unknown;\n selectedValue?: unknown;\n };\n if (typeof optionId === 'string' && typeof selectedValue === 'string') {\n picks.set(optionId, selectedValue);\n }\n }\n\n return picks;\n};\n\nconst collectFromMap = (entries: object): Map<string, string> => {\n const picks = new Map<string, string>();\n\n for (const [optionId, selectedValue] of Object.entries(entries)) {\n if (typeof selectedValue === 'string') {\n picks.set(optionId, selectedValue);\n }\n }\n\n return picks;\n};\n\nconst parseLegacyAnswer = (answer: string | null): Map<string, string> => {\n if (!answer) return new Map();\n\n try {\n const parsed: unknown = JSON.parse(answer);\n\n if (Array.isArray(parsed)) return collectFromArray(parsed);\n if (parsed !== null && typeof parsed === 'object')\n return collectFromMap(parsed);\n } catch {\n // Malformed JSON means the answer is unreadable, not that the screen fails.\n }\n\n return new Map();\n};\n\n/**\n * Render a connect-the-dots (RELACIONAR) question.\n *\n * Each option row is one pair: `option` holds the left-hand item and\n * `correctValue` its correct match. The student's picks arrive in\n * `matchingAnswers`, keyed by option id.\n *\n * Replaces a placeholder that rendered the words \"não implementado\" and ignored\n * both `question` and `result`, which left the teacher writing a comment on a\n * question whose answer was never shown.\n *\n * Returns content without wrapper (for accordion use).\n *\n * @param props - Question renderer props\n * @returns JSX element\n */\nexport const renderQuestionConnectDots = ({\n question,\n result,\n}: QuestionRendererProps): ReactNode => {\n const options = question?.options || [];\n\n const picks =\n result?.matchingAnswers && result.matchingAnswers.length > 0\n ? new Map(\n result.matchingAnswers.map((match) => [\n match.optionId,\n match.selectedValue,\n ])\n )\n : parseLegacyAnswer(result?.answer ?? null);\n\n const pairs: MatchingPair[] = options.map((option) => ({\n id: option.id,\n leftItem: option.option,\n studentValue: picks.get(option.id) ?? null,\n correctValue: option.correctValue ?? null,\n }));\n\n const shouldShowStatus =\n result?.answerStatus !== ANSWER_STATUS.PENDENTE_AVALIACAO &&\n result?.answerStatus !== ANSWER_STATUS.NAO_RESPONDIDO;\n\n // 97 = 'a' in ASCII\n const getLetterByIndex = (index: number) => String.fromCodePoint(97 + index);\n\n if (pairs.length === 0) {\n return (\n <div className=\"pt-2\">\n <Text size=\"sm\" className=\"text-text-500 italic\">\n Nenhuma opção de relacionamento disponível\n </Text>\n </div>\n );\n }\n\n return (\n <div className=\"pt-2\">\n <div className=\"flex flex-col gap-3.5\">\n {pairs.map((pair, index) => {\n const hasAnswerKey = pair.correctValue !== null;\n const hasAnswered = pair.studentValue !== null;\n\n const isStudentCorrect =\n hasAnswerKey &&\n hasAnswered &&\n normalizeValue(pair.studentValue as string) ===\n normalizeValue(pair.correctValue as string);\n\n // Correctness is only shown once the answer key is known AND the\n // answer is no longer pending.\n const canShowCorrectness = shouldShowStatus && hasAnswerKey;\n const variantCorrect = isStudentCorrect\n ? OptionStatus.CORRECT\n : OptionStatus.INCORRECT;\n\n return (\n <section\n key={pair.id || `pair-${index}`}\n className=\"flex flex-col gap-2\"\n >\n <div\n className={cn(\n 'grid grid-cols-[1fr_auto] items-center gap-4 p-2 rounded-md border',\n canShowCorrectness && hasAnswered\n ? getStatusStyles(variantCorrect)\n : ''\n )}\n >\n <HtmlMathRenderer\n content={prependLetterToHtml(\n getLetterByIndex(index),\n pair.leftItem\n )}\n className=\"text-text-900 text-sm\"\n />\n\n {canShowCorrectness && hasAnswered && (\n <div className=\"flex-shrink-0\">\n {getStatusBadge(variantCorrect)}\n </div>\n )}\n </div>\n\n {canShowCorrectness && (\n <span className=\"flex flex-row gap-2 items-center flex-wrap\">\n {hasAnswered ? (\n <>\n <Text size=\"2xs\" weight=\"normal\" color=\"text-text-800\">\n Resposta selecionada:{' '}\n {stripHtmlTags(pair.studentValue as string)}\n </Text>\n {!isStudentCorrect && (\n <Text size=\"2xs\" weight=\"normal\" color=\"text-text-800\">\n | Resposta correta:{' '}\n {stripHtmlTags(pair.correctValue as string)}\n </Text>\n )}\n </>\n ) : (\n <Text size=\"2xs\" weight=\"normal\" color=\"text-text-800\">\n Não respondida | Resposta correta:{' '}\n {stripHtmlTags(pair.correctValue as string)}\n </Text>\n )}\n </span>\n )}\n </section>\n );\n })}\n </div>\n </div>\n );\n};\n","import type { ReactNode } from 'react';\nimport { QUESTION_TYPE } from '../../components/Quiz/useQuizStore';\nimport { renderQuestionAlternative } from './alternative';\nimport { renderQuestionMultipleChoice } from './multipleChoice';\nimport { renderQuestionTrueOrFalse } from './trueOrFalse';\nimport { renderQuestionDissertative } from './dissertative';\nimport { renderQuestionFill } from './fill';\nimport { renderQuestionImage } from './image';\nimport { renderQuestionConnectDots } from './connectDots';\nimport type { QuestionRendererProps } from './types';\n\n// Re-export types\nexport type { QuestionRendererProps } from './types';\n\n// Re-export components\nexport {\n getStatusBadge,\n QuestionContainer,\n QuestionSubTitle,\n FillQuestionContent,\n} from './components';\n\n// Re-export individual renderers\nexport { renderQuestionAlternative } from './alternative';\nexport { renderQuestionMultipleChoice } from './multipleChoice';\nexport { renderQuestionTrueOrFalse } from './trueOrFalse';\nexport { renderQuestionDissertative } from './dissertative';\nexport { renderQuestionFill } from './fill';\nexport { renderQuestionImage } from './image';\nexport { renderQuestionConnectDots } from './connectDots';\n\n/**\n * Map question types to their render functions\n */\nconst questionRendererMap = {\n [QUESTION_TYPE.ALTERNATIVA]: renderQuestionAlternative,\n [QUESTION_TYPE.MULTIPLA_ESCOLHA]: renderQuestionMultipleChoice,\n [QUESTION_TYPE.VERDADEIRO_FALSO]: renderQuestionTrueOrFalse,\n [QUESTION_TYPE.DISSERTATIVA]: renderQuestionDissertative,\n [QUESTION_TYPE.PREENCHER_LACUNAS]: renderQuestionFill,\n [QUESTION_TYPE.IMAGEM]: renderQuestionImage,\n [QUESTION_TYPE.RELACIONAR]: renderQuestionConnectDots,\n};\n\n/**\n * Render question based on question type\n * @param props - Question renderer props\n * @returns Rendered question content\n */\nexport const renderQuestion = (props: QuestionRendererProps): ReactNode => {\n const { question } = props;\n const renderer = questionRendererMap[question.questionType];\n\n if (!renderer) {\n // Fallback: try to render based on options presence\n if (question.options && question.options.length > 0) {\n return renderQuestionAlternative(props);\n }\n return renderQuestionDissertative({ result: props.result });\n }\n\n // Handle renderers with different prop requirements\n switch (question.questionType) {\n case QUESTION_TYPE.DISSERTATIVA:\n return renderQuestionDissertative({ result: props.result });\n case QUESTION_TYPE.IMAGEM:\n return renderQuestionImage({\n question: props.question,\n result: props.result,\n });\n case QUESTION_TYPE.ALTERNATIVA:\n case QUESTION_TYPE.MULTIPLA_ESCOLHA:\n case QUESTION_TYPE.VERDADEIRO_FALSO:\n case QUESTION_TYPE.PREENCHER_LACUNAS:\n case QUESTION_TYPE.RELACIONAR:\n return renderer(props);\n default:\n return null;\n }\n};\n\n/**\n * Type for question renderer map\n */\nexport type QuestionRendererMap = Record<QUESTION_TYPE, () => ReactNode>;\n\n/**\n * Render from a map of renderers\n * @param renderers - Map of question type to render function\n * @param questionType - Type of question to render\n * @returns Rendered content or null\n */\nexport const renderFromMap = (\n renderers: QuestionRendererMap,\n questionType?: QUESTION_TYPE\n) => {\n if (!questionType) return null;\n const renderer = renderers[questionType];\n return renderer ? renderer() : null;\n};\n"]}
@@ -5,7 +5,7 @@ var _chunkTZA4XQPOjs = require('./chunk-TZA4XQPO.js');
5
5
 
6
6
 
7
7
 
8
- var _chunkBTZANNVOjs = require('./chunk-BTZANNVO.js');
8
+ var _chunkRQ65U5G2js = require('./chunk-RQ65U5G2.js');
9
9
 
10
10
 
11
11
  var _chunkQ3KAPJHLjs = require('./chunk-Q3KAPJHL.js');
@@ -15,7 +15,7 @@ var _chunk2WW23BFOjs = require('./chunk-2WW23BFO.js');
15
15
 
16
16
 
17
17
 
18
- var _chunkQFWY46XUjs = require('./chunk-QFWY46XU.js');
18
+ var _chunkIMDS5NQLjs = require('./chunk-IMDS5NQL.js');
19
19
 
20
20
 
21
21
  var _chunkB6K3W2ERjs = require('./chunk-B6K3W2ER.js');
@@ -524,10 +524,10 @@ function useSimulationCardDetails({
524
524
  // src/components/SimulationsPage/SimulationsDetailModal.tsx
525
525
 
526
526
  var QUESTION_STATUS_MAP = {
527
- CORRECT: _chunkBTZANNVOjs.QUESTION_STATUS.CORRETA,
528
- INCORRECT: _chunkBTZANNVOjs.QUESTION_STATUS.INCORRETA,
529
- BLANK: _chunkBTZANNVOjs.QUESTION_STATUS.EM_BRANCO,
530
- PENDING: _chunkBTZANNVOjs.QUESTION_STATUS.PENDENTE
527
+ CORRECT: _chunkRQ65U5G2js.QUESTION_STATUS.CORRETA,
528
+ INCORRECT: _chunkRQ65U5G2js.QUESTION_STATUS.INCORRETA,
529
+ BLANK: _chunkRQ65U5G2js.QUESTION_STATUS.EM_BRANCO,
530
+ PENDING: _chunkRQ65U5G2js.QUESTION_STATUS.PENDENTE
531
531
  };
532
532
  function getAnswerAccordionTitle(questionType) {
533
533
  if (questionType === "DISSERTATIVA" /* DISSERTATIVA */) {
@@ -546,7 +546,7 @@ function SimulationQuestionItem({
546
546
  index,
547
547
  onSaveComment
548
548
  }) {
549
- const badge = _chunkBTZANNVOjs.getQuestionStatusBadgeConfig.call(void 0,
549
+ const badge = _chunkRQ65U5G2js.getQuestionStatusBadgeConfig.call(void 0,
550
550
  QUESTION_STATUS_MAP[question.status]
551
551
  );
552
552
  const label = [`Quest\xE3o ${index + 1}`, question.subject].filter(Boolean).join(" - ");
@@ -576,7 +576,7 @@ function SimulationQuestionItem({
576
576
  const renderAnswerArea = () => {
577
577
  if (isImage) {
578
578
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
579
- _chunkQFWY46XUjs.ImageAnswerView,
579
+ _chunkIMDS5NQLjs.ImageAnswerView,
580
580
  {
581
581
  imageUrl: _nullishCoalesce(question.additionalContent, () => ( "")),
582
582
  correctPoint: question.correctPoint,
@@ -584,7 +584,7 @@ function SimulationQuestionItem({
584
584
  x: question.imageAnswer.coordinateX,
585
585
  y: question.imageAnswer.coordinateY
586
586
  } : null,
587
- toleranceRadius: _nullishCoalesce(question.imageTolerance, () => ( _chunkQFWY46XUjs.DEFAULT_IMAGE_TOLERANCE))
587
+ toleranceRadius: _nullishCoalesce(question.imageTolerance, () => ( _chunkIMDS5NQLjs.DEFAULT_IMAGE_TOLERANCE))
588
588
  }
589
589
  );
590
590
  }
@@ -598,7 +598,7 @@ function SimulationQuestionItem({
598
598
  ) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { size: "sm", className: "text-text-600", children: "Nenhuma resposta fornecida" }) });
599
599
  }
600
600
  if (isTrueFalse) {
601
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkBTZANNVOjs.TrueFalseStatementList, { statements: trueFalseStatements });
601
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkRQ65U5G2js.TrueFalseStatementList, { statements: trueFalseStatements });
602
602
  }
603
603
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
604
604
  _chunkB6K3W2ERjs.AlternativesList,
@@ -1083,4 +1083,4 @@ function SimulationsDetailModal({
1083
1083
 
1084
1084
 
1085
1085
  exports.buildSimulationMeta = buildSimulationMeta; exports.SectionTitle = SectionTitle; exports.DataCard = DataCard; exports.SimulationStatCards = SimulationStatCards; exports.ContentCards = ContentCards; exports.SimulationCardShell = SimulationCardShell; exports.StudentSummaryHeader = StudentSummaryHeader; exports.useSimulationCardDetails = useSimulationCardDetails; exports.SimulationQuestionItem = SimulationQuestionItem; exports.SimulationNoteRow = SimulationNoteRow; exports.SimulationAnswers = SimulationAnswers; exports.SimulationsDetailModal = SimulationsDetailModal;
1086
- //# sourceMappingURL=chunk-7XWWI5EY.js.map
1086
+ //# sourceMappingURL=chunk-YFIB3YNQ.js.map