analytica-frontend-lib 1.6.76 → 1.6.78

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 (473) hide show
  1. package/dist/AccessReportModal/index.js +4 -5
  2. package/dist/AccessReportModal/index.js.map +1 -1
  3. package/dist/AccessReportModal/index.mjs +3 -4
  4. package/dist/AccessibilityWidget/index.js +4 -5
  5. package/dist/AccessibilityWidget/index.js.map +1 -1
  6. package/dist/AccessibilityWidget/index.mjs +3 -4
  7. package/dist/Accordation/index.js +4 -4
  8. package/dist/Accordation/index.mjs +3 -3
  9. package/dist/ActivitiesHistory/index.js +48 -49
  10. package/dist/ActivitiesHistory/index.js.map +1 -1
  11. package/dist/ActivitiesHistory/index.mjs +47 -48
  12. package/dist/ActivityCardQuestionBanks/index.d.ts.map +1 -1
  13. package/dist/ActivityCardQuestionBanks/index.js +48 -49
  14. package/dist/ActivityCardQuestionBanks/index.js.map +1 -1
  15. package/dist/ActivityCardQuestionBanks/index.mjs +47 -48
  16. package/dist/ActivityCardQuestionPreview/index.js +48 -49
  17. package/dist/ActivityCardQuestionPreview/index.js.map +1 -1
  18. package/dist/ActivityCardQuestionPreview/index.mjs +47 -48
  19. package/dist/ActivityCreate/ActivityCreateContent/index.js +48 -49
  20. package/dist/ActivityCreate/ActivityCreateContent/index.js.map +1 -1
  21. package/dist/ActivityCreate/ActivityCreateContent/index.mjs +47 -48
  22. package/dist/ActivityCreate/index.js +48 -49
  23. package/dist/ActivityCreate/index.js.map +1 -1
  24. package/dist/ActivityCreate/index.mjs +47 -48
  25. package/dist/ActivityDetails/index.js +48 -49
  26. package/dist/ActivityDetails/index.js.map +1 -1
  27. package/dist/ActivityDetails/index.mjs +47 -48
  28. package/dist/ActivityFilters/index.js +48 -49
  29. package/dist/ActivityFilters/index.js.map +1 -1
  30. package/dist/ActivityFilters/index.mjs +47 -48
  31. package/dist/ActivityPageLayout/index.js +48 -49
  32. package/dist/ActivityPageLayout/index.js.map +1 -1
  33. package/dist/ActivityPageLayout/index.mjs +47 -48
  34. package/dist/ActivityPreview/index.js +48 -49
  35. package/dist/ActivityPreview/index.js.map +1 -1
  36. package/dist/ActivityPreview/index.mjs +47 -48
  37. package/dist/AlertDialog/index.d.ts.map +1 -1
  38. package/dist/AlertDialog/index.js +3 -3
  39. package/dist/AlertDialog/index.mjs +2 -2
  40. package/dist/AlertManager/index.js +48 -49
  41. package/dist/AlertManager/index.js.map +1 -1
  42. package/dist/AlertManager/index.mjs +47 -48
  43. package/dist/AlertManagerView/index.js +4 -5
  44. package/dist/AlertManagerView/index.js.map +1 -1
  45. package/dist/AlertManagerView/index.mjs +3 -4
  46. package/dist/AnnouncementModal/index.js +4 -5
  47. package/dist/AnnouncementModal/index.js.map +1 -1
  48. package/dist/AnnouncementModal/index.mjs +3 -4
  49. package/dist/AnnouncementsGate/index.js +5 -6
  50. package/dist/AnnouncementsGate/index.js.map +1 -1
  51. package/dist/AnnouncementsGate/index.mjs +4 -5
  52. package/dist/AnswerKeyModal/index.js +4 -5
  53. package/dist/AnswerKeyModal/index.js.map +1 -1
  54. package/dist/AnswerKeyModal/index.mjs +3 -4
  55. package/dist/AppHeader/index.js +8 -9
  56. package/dist/AppHeader/index.js.map +1 -1
  57. package/dist/AppHeader/index.mjs +7 -8
  58. package/dist/CalendarCard/index.js +6 -7
  59. package/dist/CalendarCard/index.js.map +1 -1
  60. package/dist/CalendarCard/index.mjs +5 -6
  61. package/dist/Card/index.d.ts.map +1 -1
  62. package/dist/Card/index.js +3 -3
  63. package/dist/Card/index.mjs +2 -2
  64. package/dist/Chat/index.js +4 -5
  65. package/dist/Chat/index.js.map +1 -1
  66. package/dist/Chat/index.mjs +3 -4
  67. package/dist/Chatbot/index.js +4 -5
  68. package/dist/Chatbot/index.js.map +1 -1
  69. package/dist/Chatbot/index.mjs +3 -4
  70. package/dist/CheckBoxGroup/index.js +48 -49
  71. package/dist/CheckBoxGroup/index.js.map +1 -1
  72. package/dist/CheckBoxGroup/index.mjs +47 -48
  73. package/dist/ComparatorChart/ComparatorView/index.js +4 -5
  74. package/dist/ComparatorChart/ComparatorView/index.js.map +1 -1
  75. package/dist/ComparatorChart/ComparatorView/index.mjs +3 -4
  76. package/dist/CorrectActivityModal/index.js +9 -10
  77. package/dist/CorrectActivityModal/index.js.map +1 -1
  78. package/dist/CorrectActivityModal/index.mjs +8 -9
  79. package/dist/DownloadButton/index.js +6 -7
  80. package/dist/DownloadButton/index.js.map +1 -1
  81. package/dist/DownloadButton/index.mjs +5 -6
  82. package/dist/DownloadModal/index.js +4 -5
  83. package/dist/DownloadModal/index.js.map +1 -1
  84. package/dist/DownloadModal/index.mjs +3 -4
  85. package/dist/DropdownMenu/index.js +4 -5
  86. package/dist/DropdownMenu/index.js.map +1 -1
  87. package/dist/DropdownMenu/index.mjs +3 -4
  88. package/dist/EnemClassroomStartModal/index.js +8 -9
  89. package/dist/EnemClassroomStartModal/index.js.map +1 -1
  90. package/dist/EnemClassroomStartModal/index.mjs +7 -8
  91. package/dist/EnemMoment/index.js +110 -94
  92. package/dist/EnemMoment/index.js.map +1 -1
  93. package/dist/EnemMoment/index.mjs +74 -58
  94. package/dist/EnemMoment/index.mjs.map +1 -1
  95. package/dist/EnemMomentStudentModal/index.js +62 -63
  96. package/dist/EnemMomentStudentModal/index.js.map +1 -1
  97. package/dist/EnemMomentStudentModal/index.mjs +48 -49
  98. package/dist/EnemMomentStudentModal/index.mjs.map +1 -1
  99. package/dist/EssayThemePicker/index.js +3 -3
  100. package/dist/EssayThemePicker/index.mjs +2 -2
  101. package/dist/ExamDetailsLayout/ExamDetailsPage/index.js +48 -49
  102. package/dist/ExamDetailsLayout/ExamDetailsPage/index.js.map +1 -1
  103. package/dist/ExamDetailsLayout/ExamDetailsPage/index.mjs +47 -48
  104. package/dist/ExamPageLayout/index.js +48 -49
  105. package/dist/ExamPageLayout/index.js.map +1 -1
  106. package/dist/ExamPageLayout/index.mjs +47 -48
  107. package/dist/Forum/index.js +6 -7
  108. package/dist/Forum/index.js.map +1 -1
  109. package/dist/Forum/index.mjs +5 -6
  110. package/dist/IconButton/index.js +2 -2
  111. package/dist/IconButton/index.mjs +1 -1
  112. package/dist/LessonCardPreview/index.js +48 -49
  113. package/dist/LessonCardPreview/index.js.map +1 -1
  114. package/dist/LessonCardPreview/index.mjs +47 -48
  115. package/dist/LessonMediaSections/index.js +48 -49
  116. package/dist/LessonMediaSections/index.js.map +1 -1
  117. package/dist/LessonMediaSections/index.mjs +47 -48
  118. package/dist/LessonsCatalog/index.js +48 -49
  119. package/dist/LessonsCatalog/index.js.map +1 -1
  120. package/dist/LessonsCatalog/index.mjs +47 -48
  121. package/dist/Modal/index.d.ts +11 -1
  122. package/dist/Modal/index.d.ts.map +1 -1
  123. package/dist/Modal/index.js +3 -4
  124. package/dist/Modal/index.js.map +1 -1
  125. package/dist/Modal/index.mjs +2 -3
  126. package/dist/MultiCheckSelect/index.js +5 -6
  127. package/dist/MultiCheckSelect/index.js.map +1 -1
  128. package/dist/MultiCheckSelect/index.mjs +4 -5
  129. package/dist/NotificationCard/index.js +6 -7
  130. package/dist/NotificationCard/index.js.map +1 -1
  131. package/dist/NotificationCard/index.mjs +5 -6
  132. package/dist/PerformanceReportModal/index.js +4 -5
  133. package/dist/PerformanceReportModal/index.js.map +1 -1
  134. package/dist/PerformanceReportModal/index.mjs +3 -4
  135. package/dist/PrintableUsersTable/index.js +48 -49
  136. package/dist/PrintableUsersTable/index.js.map +1 -1
  137. package/dist/PrintableUsersTable/index.mjs +47 -48
  138. package/dist/ProgressModal/index.js +4 -5
  139. package/dist/ProgressModal/index.js.map +1 -1
  140. package/dist/ProgressModal/index.mjs +3 -4
  141. package/dist/QuestionsPerformanceCard/index.js +49 -50
  142. package/dist/QuestionsPerformanceCard/index.js.map +1 -1
  143. package/dist/QuestionsPerformanceCard/index.mjs +48 -49
  144. package/dist/Quiz/QuizResult/index.js +4 -4
  145. package/dist/Quiz/QuizResult/index.mjs +3 -3
  146. package/dist/Quiz/index.d.ts.map +1 -1
  147. package/dist/Quiz/index.js +8 -9
  148. package/dist/Quiz/index.js.map +1 -1
  149. package/dist/Quiz/index.mjs +7 -8
  150. package/dist/QuizTimer/index.d.ts +12 -0
  151. package/dist/QuizTimer/index.d.ts.map +1 -1
  152. package/dist/QuizTimer/index.js +4 -2
  153. package/dist/QuizTimer/index.js.map +1 -1
  154. package/dist/QuizTimer/index.mjs +4 -2
  155. package/dist/RecommendedLessonCreate/index.js +48 -49
  156. package/dist/RecommendedLessonCreate/index.js.map +1 -1
  157. package/dist/RecommendedLessonCreate/index.mjs +47 -48
  158. package/dist/RecommendedLessonDetails/index.js +11 -12
  159. package/dist/RecommendedLessonDetails/index.js.map +1 -1
  160. package/dist/RecommendedLessonDetails/index.mjs +10 -11
  161. package/dist/RecommendedLessonsHistory/index.js +48 -49
  162. package/dist/RecommendedLessonsHistory/index.js.map +1 -1
  163. package/dist/RecommendedLessonsHistory/index.mjs +47 -48
  164. package/dist/ReportDetailModal/index.js +5 -6
  165. package/dist/ReportDetailModal/index.js.map +1 -1
  166. package/dist/ReportDetailModal/index.mjs +4 -5
  167. package/dist/RichEditor/index.js +6 -7
  168. package/dist/RichEditor/index.js.map +1 -1
  169. package/dist/RichEditor/index.mjs +5 -6
  170. package/dist/{RichEditorCore-62NIM7PC.js → RichEditorCore-6OT3VNJR.js} +15 -16
  171. package/dist/RichEditorCore-6OT3VNJR.js.map +1 -0
  172. package/dist/{RichEditorCore-CCO3SSSU.mjs → RichEditorCore-IF6JQHEO.mjs} +6 -7
  173. package/dist/{RichEditorCore-CCO3SSSU.mjs.map → RichEditorCore-IF6JQHEO.mjs.map} +1 -1
  174. package/dist/Search/index.js +5 -6
  175. package/dist/Search/index.js.map +1 -1
  176. package/dist/Search/index.mjs +4 -5
  177. package/dist/SendActivityModal/SendActivityModal.js +48 -49
  178. package/dist/SendActivityModal/SendActivityModal.js.map +1 -1
  179. package/dist/SendActivityModal/SendActivityModal.mjs +47 -48
  180. package/dist/SendActivityModal/index.js +48 -49
  181. package/dist/SendActivityModal/index.js.map +1 -1
  182. package/dist/SendActivityModal/index.mjs +47 -48
  183. package/dist/SendLessonModal/index.js +48 -49
  184. package/dist/SendLessonModal/index.js.map +1 -1
  185. package/dist/SendLessonModal/index.mjs +47 -48
  186. package/dist/SimulatedPerformance/SimulatedPerformanceView/index.js +48 -49
  187. package/dist/SimulatedPerformance/SimulatedPerformanceView/index.js.map +1 -1
  188. package/dist/SimulatedPerformance/SimulatedPerformanceView/index.mjs +47 -48
  189. package/dist/SimulatedPerformance/useSimulatedPerformance/index.js +6 -7
  190. package/dist/SimulatedPerformance/useSimulatedPerformance/index.js.map +1 -1
  191. package/dist/SimulatedPerformance/useSimulatedPerformance/index.mjs +5 -6
  192. package/dist/SimulatedStudentSimulationsModal/index.js +8 -9
  193. package/dist/SimulatedStudentSimulationsModal/index.js.map +1 -1
  194. package/dist/SimulatedStudentSimulationsModal/index.mjs +7 -8
  195. package/dist/SimulationSummaryCards/index.js +5 -5
  196. package/dist/SimulationSummaryCards/index.mjs +4 -4
  197. package/dist/SimulationsPage/index.js +48 -49
  198. package/dist/SimulationsPage/index.js.map +1 -1
  199. package/dist/SimulationsPage/index.mjs +47 -48
  200. package/dist/StudentLessonProgressModal/index.js +6 -7
  201. package/dist/StudentLessonProgressModal/index.js.map +1 -1
  202. package/dist/StudentLessonProgressModal/index.mjs +5 -6
  203. package/dist/StudentPerformanceDetailsModal/index.js +9 -10
  204. package/dist/StudentPerformanceDetailsModal/index.js.map +1 -1
  205. package/dist/StudentPerformanceDetailsModal/index.mjs +8 -9
  206. package/dist/Support/TicketModal/index.js +4 -5
  207. package/dist/Support/TicketModal/index.js.map +1 -1
  208. package/dist/Support/TicketModal/index.mjs +3 -4
  209. package/dist/Support/index.js +5 -6
  210. package/dist/Support/index.js.map +1 -1
  211. package/dist/Support/index.mjs +4 -5
  212. package/dist/TableProvider/index.js +48 -49
  213. package/dist/TableProvider/index.js.map +1 -1
  214. package/dist/TableProvider/index.mjs +47 -48
  215. package/dist/UnifiedDraftModelPage/index.js +48 -49
  216. package/dist/UnifiedDraftModelPage/index.js.map +1 -1
  217. package/dist/UnifiedDraftModelPage/index.mjs +47 -48
  218. package/dist/UnifiedHistoryPage/index.js +48 -49
  219. package/dist/UnifiedHistoryPage/index.js.map +1 -1
  220. package/dist/UnifiedHistoryPage/index.mjs +47 -48
  221. package/dist/VideoPlayer/index.js +7 -8
  222. package/dist/VideoPlayer/index.js.map +1 -1
  223. package/dist/VideoPlayer/index.mjs +6 -7
  224. package/dist/Whiteboard/index.js +4 -5
  225. package/dist/Whiteboard/index.js.map +1 -1
  226. package/dist/Whiteboard/index.mjs +3 -4
  227. package/dist/{chunk-EVBQNVQW.mjs → chunk-233FSBU7.mjs} +5 -5
  228. package/dist/{chunk-JU63EMZM.js → chunk-24DQFREC.js} +3 -3
  229. package/dist/{chunk-JU63EMZM.js.map → chunk-24DQFREC.js.map} +1 -1
  230. package/dist/{chunk-QFXSQKW3.mjs → chunk-254EBPMW.mjs} +22 -22
  231. package/dist/{chunk-QFXSQKW3.mjs.map → chunk-254EBPMW.mjs.map} +1 -1
  232. package/dist/{chunk-SFQVZ4KQ.js → chunk-2ETPIOCD.js} +3 -3
  233. package/dist/{chunk-SFQVZ4KQ.js.map → chunk-2ETPIOCD.js.map} +1 -1
  234. package/dist/{chunk-RCNIBRZY.js → chunk-2VTJ2PKF.js} +10 -10
  235. package/dist/{chunk-RCNIBRZY.js.map → chunk-2VTJ2PKF.js.map} +1 -1
  236. package/dist/{chunk-B35D5S4G.js → chunk-2ZMGNCMI.js} +6 -6
  237. package/dist/{chunk-B35D5S4G.js.map → chunk-2ZMGNCMI.js.map} +1 -1
  238. package/dist/{chunk-QEPKR6ZF.js → chunk-3KSRDIS2.js} +20 -20
  239. package/dist/{chunk-QEPKR6ZF.js.map → chunk-3KSRDIS2.js.map} +1 -1
  240. package/dist/{chunk-MUCV5ZWZ.mjs → chunk-3YF7H577.mjs} +15 -9
  241. package/dist/chunk-3YF7H577.mjs.map +1 -0
  242. package/dist/{chunk-MADPP2PW.mjs → chunk-3ZSI2KI5.mjs} +2 -2
  243. package/dist/{chunk-2W7PTFXS.mjs → chunk-42XKJV56.mjs} +2 -2
  244. package/dist/{chunk-M6TC6XRQ.mjs → chunk-55ESXRRM.mjs} +2 -2
  245. package/dist/{chunk-4MURAKPC.mjs → chunk-5DJVOLVX.mjs} +3 -3
  246. package/dist/{chunk-YVT7CT3G.mjs → chunk-5P6CBDZ6.mjs} +2 -2
  247. package/dist/{chunk-BHKTJCFD.js → chunk-5QUP5HKB.js} +3 -3
  248. package/dist/{chunk-BHKTJCFD.js.map → chunk-5QUP5HKB.js.map} +1 -1
  249. package/dist/{chunk-SFEU7BMY.mjs → chunk-5SFNRXZB.mjs} +2 -2
  250. package/dist/{chunk-6ALEXGAN.js → chunk-6U4AI5SE.js} +6 -6
  251. package/dist/{chunk-6ALEXGAN.js.map → chunk-6U4AI5SE.js.map} +1 -1
  252. package/dist/{chunk-KYY4U5SN.mjs → chunk-6Z5W5J44.mjs} +3 -3
  253. package/dist/{chunk-XOKBMLW3.js → chunk-7IGUYLSF.js} +3 -3
  254. package/dist/{chunk-XOKBMLW3.js.map → chunk-7IGUYLSF.js.map} +1 -1
  255. package/dist/{chunk-GVYGPJH3.mjs → chunk-AU5M2766.mjs} +2 -2
  256. package/dist/{chunk-2VODGC76.js → chunk-AVEMHDN2.js} +66 -44
  257. package/dist/chunk-AVEMHDN2.js.map +1 -0
  258. package/dist/{chunk-HIIOADV4.js → chunk-AZSRKBYS.js} +63 -71
  259. package/dist/chunk-AZSRKBYS.js.map +1 -0
  260. package/dist/{chunk-CY2VKNCB.js → chunk-BQIBVC6X.js} +5 -5
  261. package/dist/{chunk-CY2VKNCB.js.map → chunk-BQIBVC6X.js.map} +1 -1
  262. package/dist/{chunk-M77TQ5JJ.mjs → chunk-BU32E6BR.mjs} +4 -6
  263. package/dist/{chunk-M77TQ5JJ.mjs.map → chunk-BU32E6BR.mjs.map} +1 -1
  264. package/dist/{chunk-5CQVDUDO.js → chunk-BUNF5POU.js} +5 -5
  265. package/dist/{chunk-5CQVDUDO.js.map → chunk-BUNF5POU.js.map} +1 -1
  266. package/dist/{chunk-Z6UN4ZPW.mjs → chunk-BVHVYC7Q.mjs} +4 -4
  267. package/dist/{chunk-WJENEFDG.js → chunk-CAFVLHDV.js} +3 -3
  268. package/dist/{chunk-WJENEFDG.js.map → chunk-CAFVLHDV.js.map} +1 -1
  269. package/dist/{chunk-BVUPM6EY.js → chunk-CAQ2Q3ON.js} +2 -2
  270. package/dist/chunk-CAQ2Q3ON.js.map +1 -0
  271. package/dist/{chunk-QMPOGLAR.js → chunk-CIASXVWX.js} +3 -3
  272. package/dist/{chunk-QMPOGLAR.js.map → chunk-CIASXVWX.js.map} +1 -1
  273. package/dist/{chunk-APKEYUJ3.js → chunk-CIQLC2ZZ.js} +3 -3
  274. package/dist/{chunk-APKEYUJ3.js.map → chunk-CIQLC2ZZ.js.map} +1 -1
  275. package/dist/{chunk-36XPNLGQ.js → chunk-CW3FCWR4.js} +16 -16
  276. package/dist/{chunk-36XPNLGQ.js.map → chunk-CW3FCWR4.js.map} +1 -1
  277. package/dist/{chunk-J5VEAJN5.mjs → chunk-CWVUGZNK.mjs} +2 -2
  278. package/dist/{chunk-BCMDP6GK.mjs → chunk-DV3N2K3Y.mjs} +4 -4
  279. package/dist/{chunk-PP7SEERV.mjs → chunk-DXYSEBK3.mjs} +9 -3
  280. package/dist/chunk-DXYSEBK3.mjs.map +1 -0
  281. package/dist/{chunk-754M6HQ5.js → chunk-EJN4OP3D.js} +5 -5
  282. package/dist/{chunk-754M6HQ5.js.map → chunk-EJN4OP3D.js.map} +1 -1
  283. package/dist/{chunk-3IM2SBPO.js → chunk-F5K3HMKT.js} +4 -4
  284. package/dist/{chunk-3IM2SBPO.js.map → chunk-F5K3HMKT.js.map} +1 -1
  285. package/dist/{chunk-WMDSGVZ7.mjs → chunk-GCGSAS2Z.mjs} +48 -63
  286. package/dist/chunk-GCGSAS2Z.mjs.map +1 -0
  287. package/dist/{chunk-EXHGEJ4P.mjs → chunk-GKARHTZ2.mjs} +2 -2
  288. package/dist/{chunk-2VDCEEXB.js → chunk-GNKFBVW4.js} +3 -3
  289. package/dist/{chunk-2VDCEEXB.js.map → chunk-GNKFBVW4.js.map} +1 -1
  290. package/dist/{chunk-CDLOMECB.js → chunk-GTBPMNTJ.js} +3 -3
  291. package/dist/{chunk-CDLOMECB.js.map → chunk-GTBPMNTJ.js.map} +1 -1
  292. package/dist/{chunk-YQKAASWM.js → chunk-GXXDSBN2.js} +10 -4
  293. package/dist/chunk-GXXDSBN2.js.map +1 -0
  294. package/dist/{chunk-FASVPU6N.js → chunk-H3F4OCXC.js} +10 -10
  295. package/dist/{chunk-FASVPU6N.js.map → chunk-H3F4OCXC.js.map} +1 -1
  296. package/dist/{chunk-JCFN6KRS.mjs → chunk-HGWDB73S.mjs} +2 -2
  297. package/dist/{chunk-JXXMO7PY.mjs → chunk-HN5RW4C3.mjs} +3 -3
  298. package/dist/{chunk-I2A4EQPH.mjs → chunk-HQ2GXFK5.mjs} +6 -6
  299. package/dist/{chunk-BIPC6F6D.js → chunk-IDJ37MOU.js} +115 -115
  300. package/dist/{chunk-BIPC6F6D.js.map → chunk-IDJ37MOU.js.map} +1 -1
  301. package/dist/{chunk-TBJLNIOY.mjs → chunk-JBLYYTHN.mjs} +2 -2
  302. package/dist/{chunk-G4NK66PM.js → chunk-JTWXQ267.js} +24 -18
  303. package/dist/chunk-JTWXQ267.js.map +1 -0
  304. package/dist/{chunk-V6IC74KH.js → chunk-JXGLDRDC.js} +34 -34
  305. package/dist/{chunk-V6IC74KH.js.map → chunk-JXGLDRDC.js.map} +1 -1
  306. package/dist/{chunk-DDFIRQI5.mjs → chunk-K3E5VEBF.mjs} +56 -34
  307. package/dist/chunk-K3E5VEBF.mjs.map +1 -0
  308. package/dist/{chunk-MSBEWG26.js → chunk-K5O2IOI4.js} +8 -8
  309. package/dist/{chunk-MSBEWG26.js.map → chunk-K5O2IOI4.js.map} +1 -1
  310. package/dist/{chunk-SB5YGKXR.js → chunk-KBMLGQ5J.js} +49 -64
  311. package/dist/chunk-KBMLGQ5J.js.map +1 -0
  312. package/dist/chunk-KCZEKV7M.mjs +81 -0
  313. package/dist/chunk-KCZEKV7M.mjs.map +1 -0
  314. package/dist/{chunk-GRSN76BV.mjs → chunk-KH2R4STU.mjs} +3 -3
  315. package/dist/{chunk-7DQM34JT.js → chunk-KHYSXYPO.js} +23 -23
  316. package/dist/{chunk-7DQM34JT.js.map → chunk-KHYSXYPO.js.map} +1 -1
  317. package/dist/{chunk-V2YHMPRR.js → chunk-KJHBQQYJ.js} +5 -5
  318. package/dist/{chunk-V2YHMPRR.js.map → chunk-KJHBQQYJ.js.map} +1 -1
  319. package/dist/{chunk-ZF5NMQJZ.mjs → chunk-KUKLN3CK.mjs} +4 -4
  320. package/dist/{chunk-6AYB7CZH.js → chunk-KZQVGSR2.js} +2 -2
  321. package/dist/{chunk-6AYB7CZH.js.map → chunk-KZQVGSR2.js.map} +1 -1
  322. package/dist/{chunk-UAKRBNW2.mjs → chunk-L4T76OKM.mjs} +2 -2
  323. package/dist/{chunk-AUCXJ7LG.js → chunk-M3LQH7Y7.js} +3 -3
  324. package/dist/{chunk-AUCXJ7LG.js.map → chunk-M3LQH7Y7.js.map} +1 -1
  325. package/dist/{chunk-FQNJJH7F.js → chunk-MCXRKD56.js} +13 -13
  326. package/dist/{chunk-FQNJJH7F.js.map → chunk-MCXRKD56.js.map} +1 -1
  327. package/dist/{chunk-PY5WCBKN.mjs → chunk-MERKXOSI.mjs} +4 -4
  328. package/dist/{chunk-D3YDZF2G.js → chunk-N472HQPZ.js} +3 -3
  329. package/dist/{chunk-D3YDZF2G.js.map → chunk-N472HQPZ.js.map} +1 -1
  330. package/dist/{chunk-UPMYCGXR.js → chunk-N4K4X34Z.js} +4 -4
  331. package/dist/{chunk-UPMYCGXR.js.map → chunk-N4K4X34Z.js.map} +1 -1
  332. package/dist/{chunk-RU43UMEH.js → chunk-NFCNAFUK.js} +3 -3
  333. package/dist/{chunk-RU43UMEH.js.map → chunk-NFCNAFUK.js.map} +1 -1
  334. package/dist/{chunk-APCB5HWT.mjs → chunk-NLSDZJRR.mjs} +3 -3
  335. package/dist/{chunk-EWYIXCNF.mjs → chunk-NLUTNCLH.mjs} +5 -5
  336. package/dist/{chunk-OP4Q2KDS.mjs → chunk-NMKKMY5P.mjs} +2 -2
  337. package/dist/{chunk-WDPFO4II.mjs → chunk-NMPK336K.mjs} +2 -2
  338. package/dist/{chunk-X6BZ5IEV.mjs → chunk-NU7O6YKR.mjs} +2 -2
  339. package/dist/{chunk-ZABDSRX3.js → chunk-NUZ5K3CB.js} +4 -4
  340. package/dist/{chunk-ZABDSRX3.js.map → chunk-NUZ5K3CB.js.map} +1 -1
  341. package/dist/{chunk-N5ZGFYLA.js → chunk-NZX3BA62.js} +3 -3
  342. package/dist/{chunk-N5ZGFYLA.js.map → chunk-NZX3BA62.js.map} +1 -1
  343. package/dist/{chunk-2TLSEAL2.js → chunk-O4TRDWXJ.js} +3 -3
  344. package/dist/{chunk-2TLSEAL2.js.map → chunk-O4TRDWXJ.js.map} +1 -1
  345. package/dist/{chunk-CBI5F4ME.mjs → chunk-OMSSA4NO.mjs} +2 -2
  346. package/dist/{chunk-YGEEWLGJ.js → chunk-PKKXCX5B.js} +3 -3
  347. package/dist/{chunk-YGEEWLGJ.js.map → chunk-PKKXCX5B.js.map} +1 -1
  348. package/dist/{chunk-GYHD6QEA.mjs → chunk-PQGQ3EH5.mjs} +2 -2
  349. package/dist/{chunk-3LN5GQAC.js → chunk-Q6UIUYNF.js} +6 -8
  350. package/dist/chunk-Q6UIUYNF.js.map +1 -0
  351. package/dist/chunk-QLWBGM7H.js +81 -0
  352. package/dist/chunk-QLWBGM7H.js.map +1 -0
  353. package/dist/{chunk-IF5GW6ID.js → chunk-QZ5DCDZ2.js} +6 -6
  354. package/dist/{chunk-IF5GW6ID.js.map → chunk-QZ5DCDZ2.js.map} +1 -1
  355. package/dist/{chunk-LSUCENYD.mjs → chunk-RBKGHUSR.mjs} +2 -2
  356. package/dist/{chunk-PIZTI5SJ.js → chunk-RMCZ7C2C.js} +3 -3
  357. package/dist/{chunk-PIZTI5SJ.js.map → chunk-RMCZ7C2C.js.map} +1 -1
  358. package/dist/{chunk-LGQNMQOT.mjs → chunk-ROS5XGTQ.mjs} +2 -2
  359. package/dist/chunk-ROS5XGTQ.mjs.map +1 -0
  360. package/dist/{chunk-TEUERRDG.mjs → chunk-RV45XVXE.mjs} +2 -2
  361. package/dist/{chunk-FW6ZI2MC.mjs → chunk-UAFSWXN5.mjs} +2 -2
  362. package/dist/{chunk-QUGKQYAJ.mjs → chunk-ULVFVLFE.mjs} +4 -6
  363. package/dist/{chunk-QUGKQYAJ.mjs.map → chunk-ULVFVLFE.mjs.map} +1 -1
  364. package/dist/{chunk-ANLL2EIX.mjs → chunk-UWCGTH3U.mjs} +2 -2
  365. package/dist/{chunk-6R2LEEEP.mjs → chunk-UZF2D2GG.mjs} +2 -2
  366. package/dist/{chunk-62L3PMYE.mjs → chunk-V3OVMHZD.mjs} +2 -2
  367. package/dist/{chunk-UDITT3N3.mjs → chunk-V6ELDZ2B.mjs} +2 -2
  368. package/dist/{chunk-G7NUCBD3.js → chunk-VDPV24JJ.js} +3 -3
  369. package/dist/{chunk-G7NUCBD3.js.map → chunk-VDPV24JJ.js.map} +1 -1
  370. package/dist/{chunk-IJQOTS5O.mjs → chunk-VJUS3GC4.mjs} +2 -2
  371. package/dist/{chunk-ZUPP7R2K.js → chunk-W7BBVDBI.js} +17 -3
  372. package/dist/chunk-W7BBVDBI.js.map +1 -0
  373. package/dist/{chunk-EHQDQ5RV.js → chunk-XARMPXRN.js} +10 -12
  374. package/dist/chunk-XARMPXRN.js.map +1 -0
  375. package/dist/{chunk-MQEAEU6G.mjs → chunk-XGMRYNER.mjs} +2 -2
  376. package/dist/{chunk-MMQMVVD6.mjs → chunk-XLTD5J74.mjs} +3 -3
  377. package/dist/{chunk-L3ST5GUJ.js → chunk-XQUK7R7L.js} +17 -17
  378. package/dist/{chunk-L3ST5GUJ.js.map → chunk-XQUK7R7L.js.map} +1 -1
  379. package/dist/{chunk-OMBUOOMD.mjs → chunk-XYT542WD.mjs} +2 -2
  380. package/dist/{chunk-IZNABVXE.js → chunk-Y4IGIWP4.js} +3 -3
  381. package/dist/{chunk-IZNABVXE.js.map → chunk-Y4IGIWP4.js.map} +1 -1
  382. package/dist/{chunk-COWOGT7U.mjs → chunk-YZWFDSTC.mjs} +2 -2
  383. package/dist/{chunk-M2FN7CNS.mjs → chunk-Z4L2DWMX.mjs} +17 -3
  384. package/dist/chunk-Z4L2DWMX.mjs.map +1 -0
  385. package/dist/chunk-Z75J66LL.mjs +149 -0
  386. package/dist/chunk-Z75J66LL.mjs.map +1 -0
  387. package/dist/{chunk-CWIJ7RXR.js → chunk-ZHUFICW3.js} +3 -3
  388. package/dist/{chunk-CWIJ7RXR.js.map → chunk-ZHUFICW3.js.map} +1 -1
  389. package/dist/{chunk-Q5XGBTOY.js → chunk-ZNITTMV5.js} +3 -3
  390. package/dist/{chunk-Q5XGBTOY.js.map → chunk-ZNITTMV5.js.map} +1 -1
  391. package/dist/components/EnemMoment/HeaderFilters.d.ts +12 -1
  392. package/dist/components/EnemMoment/HeaderFilters.d.ts.map +1 -1
  393. package/dist/components/EnemMoment/types.d.ts +10 -1
  394. package/dist/components/EnemMoment/types.d.ts.map +1 -1
  395. package/dist/hooks/useAppContent/index.js +48 -49
  396. package/dist/hooks/useAppContent/index.js.map +1 -1
  397. package/dist/hooks/useAppContent/index.mjs +47 -48
  398. package/dist/hooks/useResizableColumns.d.ts +20 -9
  399. package/dist/hooks/useResizableColumns.d.ts.map +1 -1
  400. package/dist/hooks/useScreen/index.js +2 -2
  401. package/dist/hooks/useScreen/index.mjs +1 -1
  402. package/dist/index.js +48 -49
  403. package/dist/index.js.map +1 -1
  404. package/dist/index.mjs +47 -48
  405. package/dist/store/layoutPreferencesStore.d.ts +12 -24
  406. package/dist/store/layoutPreferencesStore.d.ts.map +1 -1
  407. package/package.json +1 -1
  408. package/dist/RichEditorCore-62NIM7PC.js.map +0 -1
  409. package/dist/chunk-2EDRPEYL.mjs +0 -157
  410. package/dist/chunk-2EDRPEYL.mjs.map +0 -1
  411. package/dist/chunk-2VODGC76.js.map +0 -1
  412. package/dist/chunk-3LN5GQAC.js.map +0 -1
  413. package/dist/chunk-BVUPM6EY.js.map +0 -1
  414. package/dist/chunk-CH6BWMSQ.js +0 -17
  415. package/dist/chunk-CH6BWMSQ.js.map +0 -1
  416. package/dist/chunk-DDFIRQI5.mjs.map +0 -1
  417. package/dist/chunk-EHQDQ5RV.js.map +0 -1
  418. package/dist/chunk-G4NK66PM.js.map +0 -1
  419. package/dist/chunk-HIIOADV4.js.map +0 -1
  420. package/dist/chunk-JSH3BQZ7.mjs +0 -17
  421. package/dist/chunk-JSH3BQZ7.mjs.map +0 -1
  422. package/dist/chunk-LGQNMQOT.mjs.map +0 -1
  423. package/dist/chunk-LMDAF6LA.mjs +0 -51
  424. package/dist/chunk-LMDAF6LA.mjs.map +0 -1
  425. package/dist/chunk-M2FN7CNS.mjs.map +0 -1
  426. package/dist/chunk-MUCV5ZWZ.mjs.map +0 -1
  427. package/dist/chunk-PJOBOBNJ.js +0 -51
  428. package/dist/chunk-PJOBOBNJ.js.map +0 -1
  429. package/dist/chunk-PP7SEERV.mjs.map +0 -1
  430. package/dist/chunk-SB5YGKXR.js.map +0 -1
  431. package/dist/chunk-WMDSGVZ7.mjs.map +0 -1
  432. package/dist/chunk-YQKAASWM.js.map +0 -1
  433. package/dist/chunk-ZUPP7R2K.js.map +0 -1
  434. /package/dist/{chunk-EVBQNVQW.mjs.map → chunk-233FSBU7.mjs.map} +0 -0
  435. /package/dist/{chunk-MADPP2PW.mjs.map → chunk-3ZSI2KI5.mjs.map} +0 -0
  436. /package/dist/{chunk-2W7PTFXS.mjs.map → chunk-42XKJV56.mjs.map} +0 -0
  437. /package/dist/{chunk-M6TC6XRQ.mjs.map → chunk-55ESXRRM.mjs.map} +0 -0
  438. /package/dist/{chunk-4MURAKPC.mjs.map → chunk-5DJVOLVX.mjs.map} +0 -0
  439. /package/dist/{chunk-YVT7CT3G.mjs.map → chunk-5P6CBDZ6.mjs.map} +0 -0
  440. /package/dist/{chunk-SFEU7BMY.mjs.map → chunk-5SFNRXZB.mjs.map} +0 -0
  441. /package/dist/{chunk-KYY4U5SN.mjs.map → chunk-6Z5W5J44.mjs.map} +0 -0
  442. /package/dist/{chunk-GVYGPJH3.mjs.map → chunk-AU5M2766.mjs.map} +0 -0
  443. /package/dist/{chunk-Z6UN4ZPW.mjs.map → chunk-BVHVYC7Q.mjs.map} +0 -0
  444. /package/dist/{chunk-J5VEAJN5.mjs.map → chunk-CWVUGZNK.mjs.map} +0 -0
  445. /package/dist/{chunk-BCMDP6GK.mjs.map → chunk-DV3N2K3Y.mjs.map} +0 -0
  446. /package/dist/{chunk-EXHGEJ4P.mjs.map → chunk-GKARHTZ2.mjs.map} +0 -0
  447. /package/dist/{chunk-JCFN6KRS.mjs.map → chunk-HGWDB73S.mjs.map} +0 -0
  448. /package/dist/{chunk-JXXMO7PY.mjs.map → chunk-HN5RW4C3.mjs.map} +0 -0
  449. /package/dist/{chunk-I2A4EQPH.mjs.map → chunk-HQ2GXFK5.mjs.map} +0 -0
  450. /package/dist/{chunk-TBJLNIOY.mjs.map → chunk-JBLYYTHN.mjs.map} +0 -0
  451. /package/dist/{chunk-GRSN76BV.mjs.map → chunk-KH2R4STU.mjs.map} +0 -0
  452. /package/dist/{chunk-ZF5NMQJZ.mjs.map → chunk-KUKLN3CK.mjs.map} +0 -0
  453. /package/dist/{chunk-UAKRBNW2.mjs.map → chunk-L4T76OKM.mjs.map} +0 -0
  454. /package/dist/{chunk-PY5WCBKN.mjs.map → chunk-MERKXOSI.mjs.map} +0 -0
  455. /package/dist/{chunk-APCB5HWT.mjs.map → chunk-NLSDZJRR.mjs.map} +0 -0
  456. /package/dist/{chunk-EWYIXCNF.mjs.map → chunk-NLUTNCLH.mjs.map} +0 -0
  457. /package/dist/{chunk-OP4Q2KDS.mjs.map → chunk-NMKKMY5P.mjs.map} +0 -0
  458. /package/dist/{chunk-WDPFO4II.mjs.map → chunk-NMPK336K.mjs.map} +0 -0
  459. /package/dist/{chunk-X6BZ5IEV.mjs.map → chunk-NU7O6YKR.mjs.map} +0 -0
  460. /package/dist/{chunk-CBI5F4ME.mjs.map → chunk-OMSSA4NO.mjs.map} +0 -0
  461. /package/dist/{chunk-GYHD6QEA.mjs.map → chunk-PQGQ3EH5.mjs.map} +0 -0
  462. /package/dist/{chunk-LSUCENYD.mjs.map → chunk-RBKGHUSR.mjs.map} +0 -0
  463. /package/dist/{chunk-TEUERRDG.mjs.map → chunk-RV45XVXE.mjs.map} +0 -0
  464. /package/dist/{chunk-FW6ZI2MC.mjs.map → chunk-UAFSWXN5.mjs.map} +0 -0
  465. /package/dist/{chunk-ANLL2EIX.mjs.map → chunk-UWCGTH3U.mjs.map} +0 -0
  466. /package/dist/{chunk-6R2LEEEP.mjs.map → chunk-UZF2D2GG.mjs.map} +0 -0
  467. /package/dist/{chunk-62L3PMYE.mjs.map → chunk-V3OVMHZD.mjs.map} +0 -0
  468. /package/dist/{chunk-UDITT3N3.mjs.map → chunk-V6ELDZ2B.mjs.map} +0 -0
  469. /package/dist/{chunk-IJQOTS5O.mjs.map → chunk-VJUS3GC4.mjs.map} +0 -0
  470. /package/dist/{chunk-MQEAEU6G.mjs.map → chunk-XGMRYNER.mjs.map} +0 -0
  471. /package/dist/{chunk-MMQMVVD6.mjs.map → chunk-XLTD5J74.mjs.map} +0 -0
  472. /package/dist/{chunk-OMBUOOMD.mjs.map → chunk-XYT542WD.mjs.map} +0 -0
  473. /package/dist/{chunk-COWOGT7U.mjs.map → chunk-YZWFDSTC.mjs.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/Chatbot/Chatbot.tsx","../src/components/Chatbot/ChatbotFab.tsx","../src/components/Chatbot/ChatbotPanel.tsx","../src/components/Chatbot/ChatbotMessageList.tsx","../src/components/Chatbot/ChatbotMessage.tsx","../src/components/Chatbot/ChatbotContentRenderer.tsx","../src/components/Chatbot/ChatbotTypingIndicator.tsx","../src/components/Chatbot/ChatbotInput.tsx","../src/components/Chatbot/ChatbotConversationList.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport ChatbotFab from './ChatbotFab';\nimport ChatbotPanel from './ChatbotPanel';\nimport ChatbotMessageList from './ChatbotMessageList';\nimport ChatbotInput from './ChatbotInput';\nimport ChatbotConversationList from './ChatbotConversationList';\nimport { createUseChatbot } from '../../hooks/useChatbot';\nimport type {\n ChatbotApiClient,\n ChatbotCurrentContext,\n ChatbotUser,\n} from '../../types/chatbot';\n\n/**\n * Props for the root Chatbot component\n */\nexport interface ChatbotProps {\n /** API client that wraps REST calls — produced by the host app */\n apiClient: ChatbotApiClient;\n /** Authenticated user (used only for UI decoration today) */\n user: ChatbotUser;\n /** Optional context sent with every outgoing message */\n currentContext?: ChatbotCurrentContext;\n /** Extra tailwind classes on the FAB */\n fabClassName?: string;\n /** Extra tailwind classes on the panel */\n panelClassName?: string;\n}\n\n/**\n * Analytica student chatbot — floating assistant available in the\n * authenticated area. Composes the Fab + Panel and owns no state of its\n * own: the `useChatbot` hook (bound to the provided `apiClient`) keeps\n * everything in one place.\n *\n * Host apps should render this inside the layout so the FAB is reachable\n * from every page. `currentContext` is a hint (activityId, questionId,\n * lessonId) produced by the host to enrich the AI prompt — it flows\n * untouched to the backend.\n */\nexport default function Chatbot({\n apiClient,\n user,\n currentContext,\n fabClassName,\n panelClassName,\n}: Readonly<ChatbotProps>) {\n // Keep the latest apiClient in a ref — any `apiClient` prop swap (e.g.,\n // after re-auth with a new token-bound client) is seen by the hook on\n // its next call without tearing down internal state.\n const apiClientRef = useRef(apiClient);\n apiClientRef.current = apiClient;\n\n // Bind the factory exactly once, passing a getter so the hook resolves\n // `apiClientRef.current` fresh on every call instead of snapshotting\n // whatever client we saw on first mount.\n const boundHookRef = useRef<ReturnType<typeof createUseChatbot> | null>(null);\n boundHookRef.current ??= createUseChatbot(() => apiClientRef.current);\n const useBoundChatbot = boundHookRef.current;\n const {\n isOpen,\n isSending,\n isLoadingHistory,\n isLoadingMessages,\n errorMessage,\n conversations,\n activeConversationId,\n messages,\n togglePanel,\n closePanel,\n selectConversation,\n startNewConversation,\n sendMessage,\n deleteConversation,\n } = useBoundChatbot();\n\n const firstName = user.name?.trim().split(/\\s+/)[0];\n const emptyHint = firstName\n ? `Olá, ${firstName}! Como posso ajudar nos seus estudos?`\n : 'Comece a conversa enviando uma pergunta.';\n\n const handleSend = (text: string): void => {\n sendMessage(text, currentContext).catch(() => undefined);\n };\n\n const fabRef = useRef<HTMLButtonElement>(null);\n const wasOpenRef = useRef(false);\n\n /**\n * Returns focus to the FAB when the panel closes.\n *\n * The panel restores focus on its own (`useModalFocus` remembers whoever\n * opened it), but when that was the FAB the remembered node is gone — the FAB\n * is unmounted while the panel is open, and the hook only restores focus to an\n * element still on the page, so focus would be left on the `<body>`.\n *\n * The `<body>` guard keeps any other opener intact: if something else still\n * holds the focus the hook already handled it, and this effect stays out of\n * the way.\n */\n useEffect(() => {\n if (isOpen) {\n wasOpenRef.current = true;\n return;\n }\n\n if (!wasOpenRef.current) return;\n wasOpenRef.current = false;\n\n const active = document.activeElement;\n if (!active || active === document.body) {\n fabRef.current?.focus();\n }\n }, [isOpen]);\n\n return (\n <>\n {!isOpen && (\n <ChatbotFab\n ref={fabRef}\n onClick={togglePanel}\n isOpen={isOpen}\n className={fabClassName}\n />\n )}\n <ChatbotPanel\n isOpen={isOpen}\n onClose={closePanel}\n onStartNew={startNewConversation}\n className={panelClassName}\n errorMessage={errorMessage}\n historySlot={\n <ChatbotConversationList\n conversations={conversations}\n activeConversationId={activeConversationId}\n isLoading={isLoadingHistory}\n onSelect={selectConversation}\n onDelete={deleteConversation}\n onStartNew={startNewConversation}\n />\n }\n messagesSlot={\n <ChatbotMessageList\n messages={messages}\n isSending={isSending}\n isLoading={isLoadingMessages}\n emptyHint={emptyHint}\n />\n }\n inputSlot={<ChatbotInput onSend={handleSend} disabled={isSending} />}\n />\n </>\n );\n}\n","import { forwardRef } from 'react';\nimport { ChatCircleDotsIcon } from '@phosphor-icons/react';\nimport Button from '../Button/Button';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\n\n/**\n * Props for the floating action button that opens the chatbot panel\n */\nexport interface ChatbotFabProps {\n /** Click handler — opens or toggles the panel */\n onClick: () => void;\n /** Whether the panel is currently open (drives aria + styling) */\n isOpen?: boolean;\n /** Optional unread count badge */\n unreadCount?: number;\n /** Extra tailwind classes */\n className?: string;\n}\n\n/**\n * Circular floating action button anchored bottom-right of the viewport.\n * Uses `fixed` so it sits on top of page scroll.\n *\n * The ref lands on the `<button>`: the FAB is unmounted while the panel is\n * open, so `Chatbot` is the one that returns focus to it on close and needs to\n * reach the freshly remounted node.\n */\nconst ChatbotFab = forwardRef<HTMLButtonElement, ChatbotFabProps>(\n ({ onClick, isOpen = false, unreadCount = 0, className }, ref) => {\n const noun =\n unreadCount === 1 ? 'mensagem não lida' : 'mensagens não lidas';\n let badgeLabel: string;\n if (unreadCount > 9) {\n badgeLabel = 'Mais de 9 mensagens não lidas';\n } else {\n badgeLabel = `${unreadCount} ${noun}`;\n }\n\n const baseAriaLabel = isOpen\n ? 'Fechar assistente'\n : 'Abrir assistente de estudos';\n const ariaLabel =\n unreadCount > 0 ? `${baseAriaLabel} (${badgeLabel})` : baseAriaLabel;\n\n return (\n <Button\n ref={ref}\n variant=\"raw\"\n onClick={onClick}\n aria-label={ariaLabel}\n aria-expanded={isOpen}\n className={cn(\n 'fixed bottom-6 right-6 z-40',\n 'flex h-14 w-14 items-center justify-center rounded-full',\n 'bg-primary-500 text-white shadow-lg',\n 'transition-all hover:bg-primary-600 active:scale-95',\n 'focus:outline-none focus:ring-2 focus:ring-primary-300',\n className\n )}\n >\n <ChatCircleDotsIcon size={26} weight=\"fill\" />\n {unreadCount > 0 && (\n <Text\n as=\"span\"\n size=\"xs\"\n weight=\"bold\"\n color=\"text-white\"\n data-testid=\"chatbot-fab-badge\"\n aria-live=\"polite\"\n aria-label={badgeLabel}\n className=\"absolute -top-1 -right-1 flex h-5 min-w-5 items-center justify-center rounded-full bg-error-500 px-1\"\n >\n {unreadCount > 9 ? '9+' : unreadCount}\n </Text>\n )}\n </Button>\n );\n }\n);\n\nChatbotFab.displayName = 'ChatbotFab';\n\nexport default ChatbotFab;\n","import { useEffect, useRef, useState, type ReactNode } from 'react';\nimport {\n CaretUpIcon,\n ListIcon,\n MinusIcon,\n PlusIcon,\n RobotIcon,\n XIcon,\n} from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport IconButton from '../IconButton/IconButton';\nimport { cn } from '../../utils/utils';\nimport { useModalFocus } from '../../hooks/useModalFocus';\nimport { useEscapeToClose } from '../../hooks/useEscapeToClose';\n\nconst HEADER_ICON_BUTTON_CLASSES =\n '!text-white hover:!bg-white/15 hover:!text-white focus-visible:!ring-white/60';\n\n/**\n * Props for the chatbot panel shell\n */\nexport interface ChatbotPanelProps {\n isOpen: boolean;\n onClose: () => void;\n onStartNew: () => void;\n /** Conversation list passed as a node so the panel stays layout-only */\n historySlot: ReactNode;\n /** Messages list passed as a node (allows easy swap during tests) */\n messagesSlot: ReactNode;\n /** Input area slot (auto-resize textarea + send button) */\n inputSlot: ReactNode;\n /** Optional error banner (string shown above the input) */\n errorMessage?: string | null;\n className?: string;\n}\n\n/**\n * Fixed drawer-like panel anchored to the bottom-right of the screen.\n * Splits into optional history sidebar + main conversation area. The\n * panel is purely presentational — state lives in the `useChatbot` hook.\n */\nexport default function ChatbotPanel({\n isOpen,\n onClose,\n onStartNew,\n historySlot,\n messagesSlot,\n inputSlot,\n errorMessage,\n className,\n}: Readonly<ChatbotPanelProps>) {\n const [showHistory, setShowHistory] = useState(false);\n const [isMinimized, setIsMinimized] = useState(false);\n const dialogRef = useRef<HTMLDialogElement>(null);\n const previousErrorRef = useRef<string | null | undefined>(errorMessage);\n\n // Reset the minimized state every time the panel is reopened so the\n // user always lands on the full conversation view.\n useEffect(() => {\n if (isOpen) {\n setIsMinimized(false);\n }\n }, [isOpen]);\n\n // Auto-expand on new errors so the role=\"alert\" banner is mounted and\n // announced by screen readers. Without this, a failed send while\n // minimized would be silent — the body (including the alert node) is\n // unmounted in the minimized state. Compare against the previous value\n // (not just truthy/falsy) so a distinct truthy error arriving after the\n // user minimized also re-expands and re-announces.\n useEffect(() => {\n const previous = previousErrorRef.current;\n if (errorMessage && errorMessage !== previous) {\n setIsMinimized(false);\n }\n previousErrorRef.current = errorMessage;\n }, [errorMessage]);\n\n /**\n * Foco e navegação pelos hooks compartilhados, os mesmos do `Modal` e do\n * `AlertDialog`: `useModalFocus` leva o foco pro diálogo ao abrir, prende Tab\n * e Shift+Tab lá dentro, torna o resto da página `inert` (um `<dialog open>`\n * sem `showModal()` não tem top layer, então nada impedia o cursor virtual do\n * leitor de tela de passear pela página atrás) e devolve o foco a quem abriu.\n *\n * `useEscapeToClose` respeita um Escape já tratado, então fechar um popover\n * de dentro do painel não fecha o painel junto.\n */\n useModalFocus(isOpen, dialogRef);\n useEscapeToClose(isOpen, onClose);\n\n if (!isOpen) return null;\n\n return (\n <dialog\n ref={dialogRef}\n open\n // Requisito do `useModalFocus`: o foco inicial vai pro próprio diálogo,\n // pro leitor anunciar \"Assistente de estudos, diálogo\" antes dos\n // controles do header.\n tabIndex={-1}\n // `open` sem `showModal()` não marca o diálogo como modal; quem prende a\n // navegação é o `inert` do hook, e este atributo é o que conta isso ao\n // leitor de tela. Mesmo par usado no `Modal` e no `AlertDialog`.\n aria-modal=\"true\"\n aria-label=\"Assistente de estudos\"\n // Height uses an inline style instead of a Tailwind arbitrary\n // class so host apps don't need to add an `@source` directive\n // for Tailwind v4 to pick up an otherwise uncommon utility.\n style={\n isMinimized ? undefined : { height: 'min(720px, calc(100vh - 3rem))' }\n }\n className={cn(\n // `<dialog open>` comes with `margin: auto` by default; `m-0`\n // resets that so the `fixed` positioning below pins the panel\n // to the bottom-right as intended. The panel sits at the same\n // bottom-right corner as the FAB (the FAB is hidden while the\n // panel is open to avoid stacking).\n 'fixed bottom-6 right-6 z-40 m-0',\n // Responsive sizing: fills small viewports without overflowing,\n // yet caps at the intended desktop footprint on larger screens.\n // The panel widens when the history sidebar is visible so the\n // main chat column keeps enough horizontal room for messages.\n 'flex w-[calc(100vw-3rem)] flex-col overflow-hidden rounded-2xl p-0',\n showHistory\n ? 'max-w-[620px] sm:max-w-[640px]'\n : 'max-w-[400px] sm:max-w-[420px]',\n 'border border-background-200 bg-background shadow-2xl',\n className\n )}\n >\n <div className=\"flex items-center justify-between gap-2 bg-primary-500 px-4 py-3 text-white\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <div\n aria-hidden=\"true\"\n className=\"flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full bg-white/15 text-white\"\n >\n <RobotIcon size={20} weight=\"fill\" />\n </div>\n <div className=\"flex min-w-0 flex-col\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"truncate leading-tight text-white\"\n >\n Assistente de estudos\n </Text>\n <Text size=\"2xs\" className=\"truncate leading-tight text-white/80\">\n Tire dúvidas enquanto estuda\n </Text>\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n {!isMinimized && (\n <>\n <IconButton\n size=\"sm\"\n aria-label={\n showHistory ? 'Ocultar histórico' : 'Mostrar histórico'\n }\n onClick={() => setShowHistory((v) => !v)}\n icon={<ListIcon size={18} />}\n className={HEADER_ICON_BUTTON_CLASSES}\n />\n <IconButton\n size=\"sm\"\n aria-label=\"Nova conversa\"\n onClick={onStartNew}\n icon={<PlusIcon size={18} />}\n className={HEADER_ICON_BUTTON_CLASSES}\n />\n </>\n )}\n <IconButton\n size=\"sm\"\n aria-label={\n isMinimized ? 'Expandir assistente' : 'Minimizar assistente'\n }\n aria-expanded={!isMinimized}\n onClick={() => setIsMinimized((v) => !v)}\n icon={\n isMinimized ? <CaretUpIcon size={18} /> : <MinusIcon size={18} />\n }\n className={HEADER_ICON_BUTTON_CLASSES}\n />\n <IconButton\n size=\"sm\"\n aria-label=\"Fechar assistente\"\n onClick={onClose}\n icon={<XIcon size={18} />}\n className={HEADER_ICON_BUTTON_CLASSES}\n />\n </div>\n </div>\n\n {!isMinimized && (\n <div className=\"flex flex-1 overflow-hidden border-t border-background-200\">\n {showHistory && (\n <div className=\"w-44 shrink-0 border-r border-background-200\">\n {historySlot}\n </div>\n )}\n <div className=\"flex flex-1 flex-col\">\n {messagesSlot}\n {errorMessage && (\n <div\n role=\"alert\"\n aria-live=\"assertive\"\n className=\"border-t border-error-200 bg-error-50 px-3 py-2\"\n >\n <Text size=\"xs\" className=\"text-error-700\">\n {errorMessage}\n </Text>\n </div>\n )}\n {inputSlot}\n </div>\n </div>\n )}\n </dialog>\n );\n}\n","import { useEffect, useRef } from 'react';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\nimport ChatbotMessage from './ChatbotMessage';\nimport ChatbotTypingIndicator from './ChatbotTypingIndicator';\nimport type { ChatbotMessage as ChatbotMessageType } from '../../types/chatbot';\n\n/**\n * Props for the scrollable list of messages\n */\nexport interface ChatbotMessageListProps {\n messages: ChatbotMessageType[];\n isSending?: boolean;\n isLoading?: boolean;\n emptyHint?: string;\n className?: string;\n}\n\n/**\n * Vertical list of messages with auto-scroll to bottom on new items.\n */\nexport default function ChatbotMessageList({\n messages,\n isSending = false,\n isLoading = false,\n emptyHint = 'Comece a conversa enviando uma pergunta.',\n className,\n}: Readonly<ChatbotMessageListProps>) {\n const bottomRef = useRef<HTMLDivElement>(null);\n const didMountRef = useRef(false);\n\n useEffect(() => {\n // Skip the scroll on initial render — otherwise scrollIntoView could\n // yank the page viewport before the panel is visible. Only auto-scroll\n // on subsequent updates (new message, typing indicator changes).\n if (!didMountRef.current) {\n didMountRef.current = true;\n return;\n }\n if (bottomRef.current) {\n bottomRef.current.scrollIntoView({ behavior: 'smooth', block: 'end' });\n }\n }, [messages.length, isSending]);\n\n const showEmpty = !isLoading && messages.length === 0 && !isSending;\n\n return (\n <div\n role=\"log\"\n aria-live=\"polite\"\n aria-relevant=\"additions\"\n className={cn(\n 'flex-1 overflow-y-auto p-4 space-y-3',\n 'bg-background-50',\n className\n )}\n >\n {showEmpty && (\n <div className=\"flex h-full items-center justify-center\">\n <Text size=\"sm\" className=\"text-center text-text-400\">\n {emptyHint}\n </Text>\n </div>\n )}\n {messages.map((msg) => (\n <ChatbotMessage key={msg.id} message={msg} />\n ))}\n {isSending && (\n <div className=\"flex justify-start\">\n <ChatbotTypingIndicator />\n </div>\n )}\n <div ref={bottomRef} />\n </div>\n );\n}\n","import { RobotIcon, UserIcon } from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\nimport ChatbotContentRenderer from './ChatbotContentRenderer';\nimport {\n CHATBOT_MESSAGE_ROLES,\n type ChatbotMessage as ChatbotMessageType,\n} from '../../types/chatbot';\n\n/**\n * Props for a single message bubble\n */\nexport interface ChatbotMessageProps {\n message: ChatbotMessageType;\n className?: string;\n /** Optional classes forwarded to the assistant markdown renderer. */\n contentClassName?: string;\n}\n\n/**\n * Format a timestamp using pt-BR locale (hour and minute only)\n */\nfunction formatTimestamp(value: Date | string): string {\n const date = value instanceof Date ? value : new Date(value);\n if (Number.isNaN(date.getTime())) return '';\n return date.toLocaleTimeString('pt-BR', {\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\n/**\n * Individual chat bubble. User messages are aligned to the right with a\n * primary background; assistant messages sit on the left in a neutral\n * background and pass through markdown / LaTeX renderers.\n */\nexport default function ChatbotMessage({\n message,\n className,\n contentClassName,\n}: Readonly<ChatbotMessageProps>) {\n const isUser = message.role === CHATBOT_MESSAGE_ROLES.USER;\n return (\n <div\n className={cn(\n 'flex w-full gap-2',\n isUser ? 'justify-end' : 'justify-start',\n className\n )}\n >\n {!isUser && (\n <div\n role=\"img\"\n aria-label=\"Mensagem do assistente\"\n className=\"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-primary-100 text-primary-600\"\n >\n <RobotIcon size={18} weight=\"fill\" aria-hidden=\"true\" />\n </div>\n )}\n <div\n className={cn(\n 'max-w-[80%] px-4 py-2',\n isUser\n ? 'rounded-2xl rounded-br-md bg-background-200 text-text-900'\n : 'rounded-2xl rounded-bl-md bg-background-100 text-text-900'\n )}\n >\n {isUser ? (\n <Text size=\"sm\" className=\"whitespace-pre-wrap break-words\">\n {message.content}\n </Text>\n ) : (\n <ChatbotContentRenderer\n content={message.content}\n className={contentClassName}\n />\n )}\n <Text size=\"2xs\" className=\"mt-1 block text-right text-text-700\">\n {formatTimestamp(message.createdAt)}\n </Text>\n </div>\n {isUser && (\n <div\n role=\"img\"\n aria-label=\"Sua mensagem\"\n className=\"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-primary-500 text-white\"\n >\n <UserIcon size={18} weight=\"fill\" aria-hidden=\"true\" />\n </div>\n )}\n </div>\n );\n}\n","import ReactMarkdown from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\nimport HtmlMathRenderer from '../HtmlMathRenderer/HtmlMathRenderer';\nimport { containsMath } from '../HtmlMathRenderer/utils';\nimport { cn } from '../../utils/utils';\n\n/**\n * Props for the assistant content renderer\n */\nexport interface ChatbotContentRendererProps {\n content: string;\n className?: string;\n}\n\n/**\n * Unambiguous math detection: only treats content as math when there is a\n * `$$...$$` display block, an explicit LaTeX block\n * (`\\begin{...}...\\end{...}`), or an inline `$...$` whose inner content\n * contains an unmistakably mathematical character (backslash / caret /\n * underscore / curly brace). This avoids false positives on prose that\n * contains currency like \"R$ 20\" or \"por $20 e o livro $ 5\".\n *\n * Instead of `matchAll` with a lazy regex (which would greedily consume\n * adjacent `$`s left-to-right and could miss a valid formula following a\n * currency span on the same line, e.g. \"Preço R$ 20 e a fórmula $x^2+1$\"),\n * we collect every `$` index and probe all pairs — detection is O(N²) in\n * the worst case but N here is the count of `$` on a single line, so in\n * practice it remains linear on realistic input.\n */\nconst MATH_CHAR_PATTERN = /[\\\\^_{}]/;\n\n/**\n * Collect every unescaped `$` index in the content.\n */\nfunction collectDollarIndices(content: string): number[] {\n const result: number[] = [];\n for (let i = 0; i < content.length; i++) {\n if (content[i] === '$' && content[i - 1] !== '\\\\') {\n result.push(i);\n }\n }\n return result;\n}\n\n/**\n * Return true if any `$...$` pair spanning a single line contains a\n * math-only character.\n */\nfunction hasInlineMathSpan(content: string, indices: number[]): boolean {\n for (let i = 0; i < indices.length; i++) {\n for (let j = i + 1; j < indices.length; j++) {\n const inner = content.slice(indices[i] + 1, indices[j]);\n if (inner.includes('\\n')) break;\n if (MATH_CHAR_PATTERN.test(inner)) return true;\n }\n }\n return false;\n}\n\nfunction looksLikeMath(content: string): boolean {\n if (!containsMath(content)) return false;\n if (/\\$\\$[\\s\\S]+?\\$\\$/.test(content)) return true;\n if (/\\\\begin\\{[^}]+\\}[\\s\\S]+?\\\\end\\{[^}]+\\}/.test(content)) return true;\n return hasInlineMathSpan(content, collectDollarIndices(content));\n}\n\n/**\n * Renders a markdown string safely. When the content contains inline LaTeX\n * ($...$ or $$...$$) we fallback to `HtmlMathRenderer`, which already\n * supports math. For plain markdown we use `react-markdown` with GFM\n * (tables, task lists, strikethrough) and no raw HTML — escape by default.\n */\nexport default function ChatbotContentRenderer({\n content,\n className,\n}: Readonly<ChatbotContentRendererProps>) {\n if (looksLikeMath(content)) {\n return (\n <HtmlMathRenderer\n content={content}\n className={cn('whitespace-pre-wrap break-words', className)}\n />\n );\n }\n\n return (\n <div\n className={cn(\n 'prose prose-sm max-w-none break-words',\n // tighten prose defaults so the bubble feels compact\n '[&>*:first-child]:mt-0 [&>*:last-child]:mb-0',\n className\n )}\n >\n {/*\n SECURITY: do NOT add `rehype-raw` or any raw-HTML plugin to\n `rehypePlugins` here. Chatbot content is untrusted (LLM output) and\n must be escaped by react-markdown's default sanitization. Enabling\n raw HTML would reintroduce an XSS vector — any change requires an\n explicit security review and a dedicated sanitization pipeline.\n */}\n <ReactMarkdown remarkPlugins={[remarkGfm]}>{content}</ReactMarkdown>\n </div>\n );\n}\n","import Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\n\n/**\n * Props for the typing indicator shown while the assistant is generating\n * a reply.\n */\nexport interface ChatbotTypingIndicatorProps {\n className?: string;\n}\n\n/**\n * Three animated dots signalling \"assistant is typing\".\n * Implemented in pure Tailwind — no additional dependencies.\n */\nexport default function ChatbotTypingIndicator({\n className,\n}: Readonly<ChatbotTypingIndicatorProps>) {\n return (\n <output\n aria-label=\"Assistente digitando\"\n className={cn(\n 'inline-flex items-center gap-1 rounded-2xl rounded-bl-md bg-background-100 px-4 py-3',\n className\n )}\n >\n <Text\n as=\"span\"\n className=\"h-2 w-2 animate-bounce rounded-full bg-text-400 [animation-delay:-0.3s]\"\n />\n <Text\n as=\"span\"\n className=\"h-2 w-2 animate-bounce rounded-full bg-text-400 [animation-delay:-0.15s]\"\n />\n <Text\n as=\"span\"\n className=\"h-2 w-2 animate-bounce rounded-full bg-text-400\"\n />\n </output>\n );\n}\n","import { useCallback, useState, type KeyboardEvent } from 'react';\nimport { PaperPlaneTiltIcon } from '@phosphor-icons/react';\nimport Button from '../Button/Button';\nimport TextArea from '../TextArea/TextArea';\nimport { cn } from '../../utils/utils';\n\nconst MIN_HEIGHT = 40;\nconst MAX_HEIGHT = 120;\n\n/**\n * Props for the chatbot text input\n */\nexport interface ChatbotInputProps {\n onSend: (text: string) => void;\n disabled?: boolean;\n placeholder?: string;\n className?: string;\n}\n\n/**\n * Auto-resizing textarea + send button. Enter sends, Shift+Enter creates a\n * new line, as is standard for chat UIs.\n *\n * Uses the library's `<TextArea>` component (design-system compliant)\n * with its built-in `autoResize` — no manual DOM height manipulation.\n */\nexport default function ChatbotInput({\n onSend,\n disabled = false,\n placeholder = 'Pergunte ao assistente...',\n className,\n}: Readonly<ChatbotInputProps>) {\n const [value, setValue] = useState('');\n\n const submit = useCallback(() => {\n const trimmed = value.trim();\n if (!trimmed || disabled) return;\n onSend(trimmed);\n setValue('');\n }, [disabled, onSend, value]);\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLTextAreaElement>) => {\n // Ignore Enter while an IME composition is in progress (e.g. typing\n // Japanese/Chinese), otherwise confirming the composition would\n // submit the message prematurely.\n if (e.nativeEvent.isComposing) return;\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n submit();\n }\n },\n [submit]\n );\n\n const isSendDisabled = disabled || value.trim().length === 0;\n\n return (\n <div\n className={cn(\n 'flex items-center gap-2 border-t border-background-200 bg-background p-3',\n className\n )}\n >\n {/* `TextArea` wraps its <textarea> in a flex column, so `flex-1`\n needs to live on this outer wrapper — passing it via\n `className` would only affect the inner element. */}\n <div className=\"flex-1 min-w-0\">\n <TextArea\n value={value}\n onChange={(e) => setValue(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n disabled={disabled}\n autoResize\n minHeight={MIN_HEIGHT}\n aria-label=\"Mensagem para o assistente\"\n className=\"w-full\"\n style={{ maxHeight: MAX_HEIGHT, overflowY: 'auto' }}\n />\n </div>\n {/* Use the library's `<Button>` with `variant=\"raw\"` (same pattern\n as `ChatbotFab`) so the send action goes through the shared\n component. `raw` keeps custom sizing/shape without inheriting\n the default solid/outline/link classes. */}\n <Button\n variant=\"raw\"\n type=\"button\"\n onClick={submit}\n disabled={isSendDisabled}\n aria-label=\"Enviar mensagem\"\n className={cn(\n 'flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full',\n 'bg-primary-500 text-white transition-colors',\n 'hover:bg-primary-600',\n 'disabled:bg-background-200 disabled:text-text-500 disabled:cursor-not-allowed',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-300'\n )}\n >\n <PaperPlaneTiltIcon size={20} weight=\"fill\" />\n </Button>\n </div>\n );\n}\n","import { TrashIcon, PlusIcon } from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport IconButton from '../IconButton/IconButton';\nimport EmptyState from '../EmptyState/EmptyState';\nimport { SkeletonText } from '../Skeleton/Skeleton';\nimport { cn } from '../../utils/utils';\nimport type { ChatbotConversation } from '../../types/chatbot';\n\n/**\n * Props for the list of past chatbot conversations\n */\nexport interface ChatbotConversationListProps {\n conversations: ChatbotConversation[];\n activeConversationId: string | null;\n isLoading?: boolean;\n onSelect: (id: string) => void;\n onDelete: (id: string) => void;\n onStartNew: () => void;\n className?: string;\n}\n\n/**\n * Format the conversation timestamp in pt-BR using the short day/month\n * form (e.g. \"17/04\"), which is enough to disambiguate conversations in\n * the sidebar without overflowing the row.\n */\nfunction formatDate(value: Date | string): string {\n const d = value instanceof Date ? value : new Date(value);\n if (Number.isNaN(d.getTime())) return '';\n return d.toLocaleDateString('pt-BR', {\n day: '2-digit',\n month: '2-digit',\n });\n}\n\n/**\n * Sidebar-like drawer listing the student's past conversations. Clicking\n * a row activates it; the trash icon calls `onDelete` — the actual\n * confirmation flow (modal, toast-undo, etc.) is delegated to the\n * consumer so behaviour can be customized per host app.\n */\nexport default function ChatbotConversationList({\n conversations,\n activeConversationId,\n isLoading = false,\n onSelect,\n onDelete,\n onStartNew,\n className,\n}: Readonly<ChatbotConversationListProps>) {\n return (\n <div\n className={cn(\n 'flex h-full flex-col border-r border-background-200 bg-background',\n className\n )}\n >\n <div className=\"p-3\">\n <Button\n variant=\"solid\"\n action=\"primary\"\n size=\"small\"\n onClick={onStartNew}\n iconLeft={<PlusIcon size={14} />}\n className=\"w-full\"\n >\n Nova conversa\n </Button>\n </div>\n\n <div className=\"flex-1 overflow-y-auto px-2 pb-2\">{renderBody()}</div>\n </div>\n );\n\n function renderBody() {\n if (isLoading) {\n return (\n <div className=\"space-y-2 px-2\">\n <SkeletonText />\n <SkeletonText />\n <SkeletonText />\n </div>\n );\n }\n\n if (conversations.length === 0) {\n return (\n <div className=\"mt-4\">\n <EmptyState\n title=\"Sem conversas ainda\"\n description=\"Comece sua primeira conversa com o assistente.\"\n />\n </div>\n );\n }\n\n return (\n <ul className=\"space-y-1\">\n {conversations.map((c) => {\n const isActive = c.id === activeConversationId;\n return (\n <li key={c.id}>\n <div\n className={cn(\n 'group flex items-start gap-2 rounded-md px-2 py-2 transition-colors',\n isActive ? 'bg-primary-50' : 'hover:bg-background-100'\n )}\n >\n <Button\n variant=\"raw\"\n onClick={() => onSelect(c.id)}\n className=\"flex-1 text-left\"\n >\n <Text\n size=\"sm\"\n weight=\"semibold\"\n className=\"truncate text-text-900\"\n >\n {c.title || 'Conversa sem título'}\n </Text>\n <Text size=\"2xs\" className=\"text-text-400\">\n {formatDate(c.lastMessageAt)}\n </Text>\n </Button>\n <IconButton\n size=\"sm\"\n aria-label={`Excluir conversa ${c.title || ''}`.trim()}\n onClick={() => onDelete(c.id)}\n icon={<TrashIcon size={14} />}\n />\n </div>\n </li>\n );\n })}\n </ul>\n );\n }\n}\n"],"mappings":"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","names":["useEffect","useRef","jsx","jsxs","useEffect","useRef","RobotIcon","jsx","jsx","jsxs","RobotIcon","jsx","jsxs","jsx","jsxs","useRef","useEffect","useState","jsx","jsxs","useState","PlusIcon","jsx","jsxs","PlusIcon","Fragment","jsx","jsxs","useRef","useEffect"]}
1
+ {"version":3,"sources":["../src/components/Chatbot/Chatbot.tsx","../src/components/Chatbot/ChatbotFab.tsx","../src/components/Chatbot/ChatbotPanel.tsx","../src/components/Chatbot/ChatbotMessageList.tsx","../src/components/Chatbot/ChatbotMessage.tsx","../src/components/Chatbot/ChatbotContentRenderer.tsx","../src/components/Chatbot/ChatbotTypingIndicator.tsx","../src/components/Chatbot/ChatbotInput.tsx","../src/components/Chatbot/ChatbotConversationList.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport ChatbotFab from './ChatbotFab';\nimport ChatbotPanel from './ChatbotPanel';\nimport ChatbotMessageList from './ChatbotMessageList';\nimport ChatbotInput from './ChatbotInput';\nimport ChatbotConversationList from './ChatbotConversationList';\nimport { createUseChatbot } from '../../hooks/useChatbot';\nimport type {\n ChatbotApiClient,\n ChatbotCurrentContext,\n ChatbotUser,\n} from '../../types/chatbot';\n\n/**\n * Props for the root Chatbot component\n */\nexport interface ChatbotProps {\n /** API client that wraps REST calls — produced by the host app */\n apiClient: ChatbotApiClient;\n /** Authenticated user (used only for UI decoration today) */\n user: ChatbotUser;\n /** Optional context sent with every outgoing message */\n currentContext?: ChatbotCurrentContext;\n /** Extra tailwind classes on the FAB */\n fabClassName?: string;\n /** Extra tailwind classes on the panel */\n panelClassName?: string;\n}\n\n/**\n * Analytica student chatbot — floating assistant available in the\n * authenticated area. Composes the Fab + Panel and owns no state of its\n * own: the `useChatbot` hook (bound to the provided `apiClient`) keeps\n * everything in one place.\n *\n * Host apps should render this inside the layout so the FAB is reachable\n * from every page. `currentContext` is a hint (activityId, questionId,\n * lessonId) produced by the host to enrich the AI prompt — it flows\n * untouched to the backend.\n */\nexport default function Chatbot({\n apiClient,\n user,\n currentContext,\n fabClassName,\n panelClassName,\n}: Readonly<ChatbotProps>) {\n // Keep the latest apiClient in a ref — any `apiClient` prop swap (e.g.,\n // after re-auth with a new token-bound client) is seen by the hook on\n // its next call without tearing down internal state.\n const apiClientRef = useRef(apiClient);\n apiClientRef.current = apiClient;\n\n // Bind the factory exactly once, passing a getter so the hook resolves\n // `apiClientRef.current` fresh on every call instead of snapshotting\n // whatever client we saw on first mount.\n const boundHookRef = useRef<ReturnType<typeof createUseChatbot> | null>(null);\n boundHookRef.current ??= createUseChatbot(() => apiClientRef.current);\n const useBoundChatbot = boundHookRef.current;\n const {\n isOpen,\n isSending,\n isLoadingHistory,\n isLoadingMessages,\n errorMessage,\n conversations,\n activeConversationId,\n messages,\n togglePanel,\n closePanel,\n selectConversation,\n startNewConversation,\n sendMessage,\n deleteConversation,\n } = useBoundChatbot();\n\n const firstName = user.name?.trim().split(/\\s+/)[0];\n const emptyHint = firstName\n ? `Olá, ${firstName}! Como posso ajudar nos seus estudos?`\n : 'Comece a conversa enviando uma pergunta.';\n\n const handleSend = (text: string): void => {\n sendMessage(text, currentContext).catch(() => undefined);\n };\n\n const fabRef = useRef<HTMLButtonElement>(null);\n const wasOpenRef = useRef(false);\n\n /**\n * Returns focus to the FAB when the panel closes.\n *\n * The panel restores focus on its own (`useModalFocus` remembers whoever\n * opened it), but when that was the FAB the remembered node is gone — the FAB\n * is unmounted while the panel is open, and the hook only restores focus to an\n * element still on the page, so focus would be left on the `<body>`.\n *\n * The `<body>` guard keeps any other opener intact: if something else still\n * holds the focus the hook already handled it, and this effect stays out of\n * the way.\n */\n useEffect(() => {\n if (isOpen) {\n wasOpenRef.current = true;\n return;\n }\n\n if (!wasOpenRef.current) return;\n wasOpenRef.current = false;\n\n const active = document.activeElement;\n if (!active || active === document.body) {\n fabRef.current?.focus();\n }\n }, [isOpen]);\n\n return (\n <>\n {!isOpen && (\n <ChatbotFab\n ref={fabRef}\n onClick={togglePanel}\n isOpen={isOpen}\n className={fabClassName}\n />\n )}\n <ChatbotPanel\n isOpen={isOpen}\n onClose={closePanel}\n onStartNew={startNewConversation}\n className={panelClassName}\n errorMessage={errorMessage}\n historySlot={\n <ChatbotConversationList\n conversations={conversations}\n activeConversationId={activeConversationId}\n isLoading={isLoadingHistory}\n onSelect={selectConversation}\n onDelete={deleteConversation}\n onStartNew={startNewConversation}\n />\n }\n messagesSlot={\n <ChatbotMessageList\n messages={messages}\n isSending={isSending}\n isLoading={isLoadingMessages}\n emptyHint={emptyHint}\n />\n }\n inputSlot={<ChatbotInput onSend={handleSend} disabled={isSending} />}\n />\n </>\n );\n}\n","import { forwardRef } from 'react';\nimport { ChatCircleDotsIcon } from '@phosphor-icons/react';\nimport Button from '../Button/Button';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\n\n/**\n * Props for the floating action button that opens the chatbot panel\n */\nexport interface ChatbotFabProps {\n /** Click handler — opens or toggles the panel */\n onClick: () => void;\n /** Whether the panel is currently open (drives aria + styling) */\n isOpen?: boolean;\n /** Optional unread count badge */\n unreadCount?: number;\n /** Extra tailwind classes */\n className?: string;\n}\n\n/**\n * Circular floating action button anchored bottom-right of the viewport.\n * Uses `fixed` so it sits on top of page scroll.\n *\n * The ref lands on the `<button>`: the FAB is unmounted while the panel is\n * open, so `Chatbot` is the one that returns focus to it on close and needs to\n * reach the freshly remounted node.\n */\nconst ChatbotFab = forwardRef<HTMLButtonElement, ChatbotFabProps>(\n ({ onClick, isOpen = false, unreadCount = 0, className }, ref) => {\n const noun =\n unreadCount === 1 ? 'mensagem não lida' : 'mensagens não lidas';\n let badgeLabel: string;\n if (unreadCount > 9) {\n badgeLabel = 'Mais de 9 mensagens não lidas';\n } else {\n badgeLabel = `${unreadCount} ${noun}`;\n }\n\n const baseAriaLabel = isOpen\n ? 'Fechar assistente'\n : 'Abrir assistente de estudos';\n const ariaLabel =\n unreadCount > 0 ? `${baseAriaLabel} (${badgeLabel})` : baseAriaLabel;\n\n return (\n <Button\n ref={ref}\n variant=\"raw\"\n onClick={onClick}\n aria-label={ariaLabel}\n aria-expanded={isOpen}\n className={cn(\n 'fixed bottom-6 right-6 z-40',\n 'flex h-14 w-14 items-center justify-center rounded-full',\n 'bg-primary-500 text-white shadow-lg',\n 'transition-all hover:bg-primary-600 active:scale-95',\n 'focus:outline-none focus:ring-2 focus:ring-primary-300',\n className\n )}\n >\n <ChatCircleDotsIcon size={26} weight=\"fill\" />\n {unreadCount > 0 && (\n <Text\n as=\"span\"\n size=\"xs\"\n weight=\"bold\"\n color=\"text-white\"\n data-testid=\"chatbot-fab-badge\"\n aria-live=\"polite\"\n aria-label={badgeLabel}\n className=\"absolute -top-1 -right-1 flex h-5 min-w-5 items-center justify-center rounded-full bg-error-500 px-1\"\n >\n {unreadCount > 9 ? '9+' : unreadCount}\n </Text>\n )}\n </Button>\n );\n }\n);\n\nChatbotFab.displayName = 'ChatbotFab';\n\nexport default ChatbotFab;\n","import { useEffect, useRef, useState, type ReactNode } from 'react';\nimport {\n CaretUpIcon,\n ListIcon,\n MinusIcon,\n PlusIcon,\n RobotIcon,\n XIcon,\n} from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport IconButton from '../IconButton/IconButton';\nimport { cn } from '../../utils/utils';\nimport { useModalFocus } from '../../hooks/useModalFocus';\nimport { useEscapeToClose } from '../../hooks/useEscapeToClose';\n\nconst HEADER_ICON_BUTTON_CLASSES =\n '!text-white hover:!bg-white/15 hover:!text-white focus-visible:!ring-white/60';\n\n/**\n * Props for the chatbot panel shell\n */\nexport interface ChatbotPanelProps {\n isOpen: boolean;\n onClose: () => void;\n onStartNew: () => void;\n /** Conversation list passed as a node so the panel stays layout-only */\n historySlot: ReactNode;\n /** Messages list passed as a node (allows easy swap during tests) */\n messagesSlot: ReactNode;\n /** Input area slot (auto-resize textarea + send button) */\n inputSlot: ReactNode;\n /** Optional error banner (string shown above the input) */\n errorMessage?: string | null;\n className?: string;\n}\n\n/**\n * Fixed drawer-like panel anchored to the bottom-right of the screen.\n * Splits into optional history sidebar + main conversation area. The\n * panel is purely presentational — state lives in the `useChatbot` hook.\n */\nexport default function ChatbotPanel({\n isOpen,\n onClose,\n onStartNew,\n historySlot,\n messagesSlot,\n inputSlot,\n errorMessage,\n className,\n}: Readonly<ChatbotPanelProps>) {\n const [showHistory, setShowHistory] = useState(false);\n const [isMinimized, setIsMinimized] = useState(false);\n const dialogRef = useRef<HTMLDialogElement>(null);\n const previousErrorRef = useRef<string | null | undefined>(errorMessage);\n\n // Reset the minimized state every time the panel is reopened so the\n // user always lands on the full conversation view.\n useEffect(() => {\n if (isOpen) {\n setIsMinimized(false);\n }\n }, [isOpen]);\n\n // Auto-expand on new errors so the role=\"alert\" banner is mounted and\n // announced by screen readers. Without this, a failed send while\n // minimized would be silent — the body (including the alert node) is\n // unmounted in the minimized state. Compare against the previous value\n // (not just truthy/falsy) so a distinct truthy error arriving after the\n // user minimized also re-expands and re-announces.\n useEffect(() => {\n const previous = previousErrorRef.current;\n if (errorMessage && errorMessage !== previous) {\n setIsMinimized(false);\n }\n previousErrorRef.current = errorMessage;\n }, [errorMessage]);\n\n /**\n * Foco e navegação pelos hooks compartilhados, os mesmos do `Modal` e do\n * `AlertDialog`: `useModalFocus` leva o foco pro diálogo ao abrir, prende Tab\n * e Shift+Tab lá dentro, torna o resto da página `inert` (um `<dialog open>`\n * sem `showModal()` não tem top layer, então nada impedia o cursor virtual do\n * leitor de tela de passear pela página atrás) e devolve o foco a quem abriu.\n *\n * `useEscapeToClose` respeita um Escape já tratado, então fechar um popover\n * de dentro do painel não fecha o painel junto.\n */\n useModalFocus(isOpen, dialogRef);\n useEscapeToClose(isOpen, onClose);\n\n if (!isOpen) return null;\n\n return (\n <dialog\n ref={dialogRef}\n open\n // Requisito do `useModalFocus`: o foco inicial vai pro próprio diálogo,\n // pro leitor anunciar \"Assistente de estudos, diálogo\" antes dos\n // controles do header.\n tabIndex={-1}\n // `open` sem `showModal()` não marca o diálogo como modal; quem prende a\n // navegação é o `inert` do hook, e este atributo é o que conta isso ao\n // leitor de tela. Mesmo par usado no `Modal` e no `AlertDialog`.\n aria-modal=\"true\"\n aria-label=\"Assistente de estudos\"\n // Height uses an inline style instead of a Tailwind arbitrary\n // class so host apps don't need to add an `@source` directive\n // for Tailwind v4 to pick up an otherwise uncommon utility.\n style={\n isMinimized ? undefined : { height: 'min(720px, calc(100vh - 3rem))' }\n }\n className={cn(\n // `<dialog open>` comes with `margin: auto` by default; `m-0`\n // resets that so the `fixed` positioning below pins the panel\n // to the bottom-right as intended. The panel sits at the same\n // bottom-right corner as the FAB (the FAB is hidden while the\n // panel is open to avoid stacking).\n 'fixed bottom-6 right-6 z-40 m-0',\n // Responsive sizing: fills small viewports without overflowing,\n // yet caps at the intended desktop footprint on larger screens.\n // The panel widens when the history sidebar is visible so the\n // main chat column keeps enough horizontal room for messages.\n 'flex w-[calc(100vw-3rem)] flex-col overflow-hidden rounded-2xl p-0',\n showHistory\n ? 'max-w-[620px] sm:max-w-[640px]'\n : 'max-w-[400px] sm:max-w-[420px]',\n 'border border-background-200 bg-background shadow-2xl',\n className\n )}\n >\n <div className=\"flex items-center justify-between gap-2 bg-primary-500 px-4 py-3 text-white\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <div\n aria-hidden=\"true\"\n className=\"flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full bg-white/15 text-white\"\n >\n <RobotIcon size={20} weight=\"fill\" />\n </div>\n <div className=\"flex min-w-0 flex-col\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"truncate leading-tight text-white\"\n >\n Assistente de estudos\n </Text>\n <Text size=\"2xs\" className=\"truncate leading-tight text-white/80\">\n Tire dúvidas enquanto estuda\n </Text>\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n {!isMinimized && (\n <>\n <IconButton\n size=\"sm\"\n aria-label={\n showHistory ? 'Ocultar histórico' : 'Mostrar histórico'\n }\n onClick={() => setShowHistory((v) => !v)}\n icon={<ListIcon size={18} />}\n className={HEADER_ICON_BUTTON_CLASSES}\n />\n <IconButton\n size=\"sm\"\n aria-label=\"Nova conversa\"\n onClick={onStartNew}\n icon={<PlusIcon size={18} />}\n className={HEADER_ICON_BUTTON_CLASSES}\n />\n </>\n )}\n <IconButton\n size=\"sm\"\n aria-label={\n isMinimized ? 'Expandir assistente' : 'Minimizar assistente'\n }\n aria-expanded={!isMinimized}\n onClick={() => setIsMinimized((v) => !v)}\n icon={\n isMinimized ? <CaretUpIcon size={18} /> : <MinusIcon size={18} />\n }\n className={HEADER_ICON_BUTTON_CLASSES}\n />\n <IconButton\n size=\"sm\"\n aria-label=\"Fechar assistente\"\n onClick={onClose}\n icon={<XIcon size={18} />}\n className={HEADER_ICON_BUTTON_CLASSES}\n />\n </div>\n </div>\n\n {!isMinimized && (\n <div className=\"flex flex-1 overflow-hidden border-t border-background-200\">\n {showHistory && (\n <div className=\"w-44 shrink-0 border-r border-background-200\">\n {historySlot}\n </div>\n )}\n <div className=\"flex flex-1 flex-col\">\n {messagesSlot}\n {errorMessage && (\n <div\n role=\"alert\"\n aria-live=\"assertive\"\n className=\"border-t border-error-200 bg-error-50 px-3 py-2\"\n >\n <Text size=\"xs\" className=\"text-error-700\">\n {errorMessage}\n </Text>\n </div>\n )}\n {inputSlot}\n </div>\n </div>\n )}\n </dialog>\n );\n}\n","import { useEffect, useRef } from 'react';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\nimport ChatbotMessage from './ChatbotMessage';\nimport ChatbotTypingIndicator from './ChatbotTypingIndicator';\nimport type { ChatbotMessage as ChatbotMessageType } from '../../types/chatbot';\n\n/**\n * Props for the scrollable list of messages\n */\nexport interface ChatbotMessageListProps {\n messages: ChatbotMessageType[];\n isSending?: boolean;\n isLoading?: boolean;\n emptyHint?: string;\n className?: string;\n}\n\n/**\n * Vertical list of messages with auto-scroll to bottom on new items.\n */\nexport default function ChatbotMessageList({\n messages,\n isSending = false,\n isLoading = false,\n emptyHint = 'Comece a conversa enviando uma pergunta.',\n className,\n}: Readonly<ChatbotMessageListProps>) {\n const bottomRef = useRef<HTMLDivElement>(null);\n const didMountRef = useRef(false);\n\n useEffect(() => {\n // Skip the scroll on initial render — otherwise scrollIntoView could\n // yank the page viewport before the panel is visible. Only auto-scroll\n // on subsequent updates (new message, typing indicator changes).\n if (!didMountRef.current) {\n didMountRef.current = true;\n return;\n }\n if (bottomRef.current) {\n bottomRef.current.scrollIntoView({ behavior: 'smooth', block: 'end' });\n }\n }, [messages.length, isSending]);\n\n const showEmpty = !isLoading && messages.length === 0 && !isSending;\n\n return (\n <div\n role=\"log\"\n aria-live=\"polite\"\n aria-relevant=\"additions\"\n className={cn(\n 'flex-1 overflow-y-auto p-4 space-y-3',\n 'bg-background-50',\n className\n )}\n >\n {showEmpty && (\n <div className=\"flex h-full items-center justify-center\">\n <Text size=\"sm\" className=\"text-center text-text-400\">\n {emptyHint}\n </Text>\n </div>\n )}\n {messages.map((msg) => (\n <ChatbotMessage key={msg.id} message={msg} />\n ))}\n {isSending && (\n <div className=\"flex justify-start\">\n <ChatbotTypingIndicator />\n </div>\n )}\n <div ref={bottomRef} />\n </div>\n );\n}\n","import { RobotIcon, UserIcon } from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\nimport ChatbotContentRenderer from './ChatbotContentRenderer';\nimport {\n CHATBOT_MESSAGE_ROLES,\n type ChatbotMessage as ChatbotMessageType,\n} from '../../types/chatbot';\n\n/**\n * Props for a single message bubble\n */\nexport interface ChatbotMessageProps {\n message: ChatbotMessageType;\n className?: string;\n /** Optional classes forwarded to the assistant markdown renderer. */\n contentClassName?: string;\n}\n\n/**\n * Format a timestamp using pt-BR locale (hour and minute only)\n */\nfunction formatTimestamp(value: Date | string): string {\n const date = value instanceof Date ? value : new Date(value);\n if (Number.isNaN(date.getTime())) return '';\n return date.toLocaleTimeString('pt-BR', {\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\n/**\n * Individual chat bubble. User messages are aligned to the right with a\n * primary background; assistant messages sit on the left in a neutral\n * background and pass through markdown / LaTeX renderers.\n */\nexport default function ChatbotMessage({\n message,\n className,\n contentClassName,\n}: Readonly<ChatbotMessageProps>) {\n const isUser = message.role === CHATBOT_MESSAGE_ROLES.USER;\n return (\n <div\n className={cn(\n 'flex w-full gap-2',\n isUser ? 'justify-end' : 'justify-start',\n className\n )}\n >\n {!isUser && (\n <div\n role=\"img\"\n aria-label=\"Mensagem do assistente\"\n className=\"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-primary-100 text-primary-600\"\n >\n <RobotIcon size={18} weight=\"fill\" aria-hidden=\"true\" />\n </div>\n )}\n <div\n className={cn(\n 'max-w-[80%] px-4 py-2',\n isUser\n ? 'rounded-2xl rounded-br-md bg-background-200 text-text-900'\n : 'rounded-2xl rounded-bl-md bg-background-100 text-text-900'\n )}\n >\n {isUser ? (\n <Text size=\"sm\" className=\"whitespace-pre-wrap break-words\">\n {message.content}\n </Text>\n ) : (\n <ChatbotContentRenderer\n content={message.content}\n className={contentClassName}\n />\n )}\n <Text size=\"2xs\" className=\"mt-1 block text-right text-text-700\">\n {formatTimestamp(message.createdAt)}\n </Text>\n </div>\n {isUser && (\n <div\n role=\"img\"\n aria-label=\"Sua mensagem\"\n className=\"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-primary-500 text-white\"\n >\n <UserIcon size={18} weight=\"fill\" aria-hidden=\"true\" />\n </div>\n )}\n </div>\n );\n}\n","import ReactMarkdown from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\nimport HtmlMathRenderer from '../HtmlMathRenderer/HtmlMathRenderer';\nimport { containsMath } from '../HtmlMathRenderer/utils';\nimport { cn } from '../../utils/utils';\n\n/**\n * Props for the assistant content renderer\n */\nexport interface ChatbotContentRendererProps {\n content: string;\n className?: string;\n}\n\n/**\n * Unambiguous math detection: only treats content as math when there is a\n * `$$...$$` display block, an explicit LaTeX block\n * (`\\begin{...}...\\end{...}`), or an inline `$...$` whose inner content\n * contains an unmistakably mathematical character (backslash / caret /\n * underscore / curly brace). This avoids false positives on prose that\n * contains currency like \"R$ 20\" or \"por $20 e o livro $ 5\".\n *\n * Instead of `matchAll` with a lazy regex (which would greedily consume\n * adjacent `$`s left-to-right and could miss a valid formula following a\n * currency span on the same line, e.g. \"Preço R$ 20 e a fórmula $x^2+1$\"),\n * we collect every `$` index and probe all pairs — detection is O(N²) in\n * the worst case but N here is the count of `$` on a single line, so in\n * practice it remains linear on realistic input.\n */\nconst MATH_CHAR_PATTERN = /[\\\\^_{}]/;\n\n/**\n * Collect every unescaped `$` index in the content.\n */\nfunction collectDollarIndices(content: string): number[] {\n const result: number[] = [];\n for (let i = 0; i < content.length; i++) {\n if (content[i] === '$' && content[i - 1] !== '\\\\') {\n result.push(i);\n }\n }\n return result;\n}\n\n/**\n * Return true if any `$...$` pair spanning a single line contains a\n * math-only character.\n */\nfunction hasInlineMathSpan(content: string, indices: number[]): boolean {\n for (let i = 0; i < indices.length; i++) {\n for (let j = i + 1; j < indices.length; j++) {\n const inner = content.slice(indices[i] + 1, indices[j]);\n if (inner.includes('\\n')) break;\n if (MATH_CHAR_PATTERN.test(inner)) return true;\n }\n }\n return false;\n}\n\nfunction looksLikeMath(content: string): boolean {\n if (!containsMath(content)) return false;\n if (/\\$\\$[\\s\\S]+?\\$\\$/.test(content)) return true;\n if (/\\\\begin\\{[^}]+\\}[\\s\\S]+?\\\\end\\{[^}]+\\}/.test(content)) return true;\n return hasInlineMathSpan(content, collectDollarIndices(content));\n}\n\n/**\n * Renders a markdown string safely. When the content contains inline LaTeX\n * ($...$ or $$...$$) we fallback to `HtmlMathRenderer`, which already\n * supports math. For plain markdown we use `react-markdown` with GFM\n * (tables, task lists, strikethrough) and no raw HTML — escape by default.\n */\nexport default function ChatbotContentRenderer({\n content,\n className,\n}: Readonly<ChatbotContentRendererProps>) {\n if (looksLikeMath(content)) {\n return (\n <HtmlMathRenderer\n content={content}\n className={cn('whitespace-pre-wrap break-words', className)}\n />\n );\n }\n\n return (\n <div\n className={cn(\n 'prose prose-sm max-w-none break-words',\n // tighten prose defaults so the bubble feels compact\n '[&>*:first-child]:mt-0 [&>*:last-child]:mb-0',\n className\n )}\n >\n {/*\n SECURITY: do NOT add `rehype-raw` or any raw-HTML plugin to\n `rehypePlugins` here. Chatbot content is untrusted (LLM output) and\n must be escaped by react-markdown's default sanitization. Enabling\n raw HTML would reintroduce an XSS vector — any change requires an\n explicit security review and a dedicated sanitization pipeline.\n */}\n <ReactMarkdown remarkPlugins={[remarkGfm]}>{content}</ReactMarkdown>\n </div>\n );\n}\n","import Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\n\n/**\n * Props for the typing indicator shown while the assistant is generating\n * a reply.\n */\nexport interface ChatbotTypingIndicatorProps {\n className?: string;\n}\n\n/**\n * Three animated dots signalling \"assistant is typing\".\n * Implemented in pure Tailwind — no additional dependencies.\n */\nexport default function ChatbotTypingIndicator({\n className,\n}: Readonly<ChatbotTypingIndicatorProps>) {\n return (\n <output\n aria-label=\"Assistente digitando\"\n className={cn(\n 'inline-flex items-center gap-1 rounded-2xl rounded-bl-md bg-background-100 px-4 py-3',\n className\n )}\n >\n <Text\n as=\"span\"\n className=\"h-2 w-2 animate-bounce rounded-full bg-text-400 [animation-delay:-0.3s]\"\n />\n <Text\n as=\"span\"\n className=\"h-2 w-2 animate-bounce rounded-full bg-text-400 [animation-delay:-0.15s]\"\n />\n <Text\n as=\"span\"\n className=\"h-2 w-2 animate-bounce rounded-full bg-text-400\"\n />\n </output>\n );\n}\n","import { useCallback, useState, type KeyboardEvent } from 'react';\nimport { PaperPlaneTiltIcon } from '@phosphor-icons/react';\nimport Button from '../Button/Button';\nimport TextArea from '../TextArea/TextArea';\nimport { cn } from '../../utils/utils';\n\nconst MIN_HEIGHT = 40;\nconst MAX_HEIGHT = 120;\n\n/**\n * Props for the chatbot text input\n */\nexport interface ChatbotInputProps {\n onSend: (text: string) => void;\n disabled?: boolean;\n placeholder?: string;\n className?: string;\n}\n\n/**\n * Auto-resizing textarea + send button. Enter sends, Shift+Enter creates a\n * new line, as is standard for chat UIs.\n *\n * Uses the library's `<TextArea>` component (design-system compliant)\n * with its built-in `autoResize` — no manual DOM height manipulation.\n */\nexport default function ChatbotInput({\n onSend,\n disabled = false,\n placeholder = 'Pergunte ao assistente...',\n className,\n}: Readonly<ChatbotInputProps>) {\n const [value, setValue] = useState('');\n\n const submit = useCallback(() => {\n const trimmed = value.trim();\n if (!trimmed || disabled) return;\n onSend(trimmed);\n setValue('');\n }, [disabled, onSend, value]);\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLTextAreaElement>) => {\n // Ignore Enter while an IME composition is in progress (e.g. typing\n // Japanese/Chinese), otherwise confirming the composition would\n // submit the message prematurely.\n if (e.nativeEvent.isComposing) return;\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n submit();\n }\n },\n [submit]\n );\n\n const isSendDisabled = disabled || value.trim().length === 0;\n\n return (\n <div\n className={cn(\n 'flex items-center gap-2 border-t border-background-200 bg-background p-3',\n className\n )}\n >\n {/* `TextArea` wraps its <textarea> in a flex column, so `flex-1`\n needs to live on this outer wrapper — passing it via\n `className` would only affect the inner element. */}\n <div className=\"flex-1 min-w-0\">\n <TextArea\n value={value}\n onChange={(e) => setValue(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n disabled={disabled}\n autoResize\n minHeight={MIN_HEIGHT}\n aria-label=\"Mensagem para o assistente\"\n className=\"w-full\"\n style={{ maxHeight: MAX_HEIGHT, overflowY: 'auto' }}\n />\n </div>\n {/* Use the library's `<Button>` with `variant=\"raw\"` (same pattern\n as `ChatbotFab`) so the send action goes through the shared\n component. `raw` keeps custom sizing/shape without inheriting\n the default solid/outline/link classes. */}\n <Button\n variant=\"raw\"\n type=\"button\"\n onClick={submit}\n disabled={isSendDisabled}\n aria-label=\"Enviar mensagem\"\n className={cn(\n 'flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full',\n 'bg-primary-500 text-white transition-colors',\n 'hover:bg-primary-600',\n 'disabled:bg-background-200 disabled:text-text-500 disabled:cursor-not-allowed',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-300'\n )}\n >\n <PaperPlaneTiltIcon size={20} weight=\"fill\" />\n </Button>\n </div>\n );\n}\n","import { TrashIcon, PlusIcon } from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport IconButton from '../IconButton/IconButton';\nimport EmptyState from '../EmptyState/EmptyState';\nimport { SkeletonText } from '../Skeleton/Skeleton';\nimport { cn } from '../../utils/utils';\nimport type { ChatbotConversation } from '../../types/chatbot';\n\n/**\n * Props for the list of past chatbot conversations\n */\nexport interface ChatbotConversationListProps {\n conversations: ChatbotConversation[];\n activeConversationId: string | null;\n isLoading?: boolean;\n onSelect: (id: string) => void;\n onDelete: (id: string) => void;\n onStartNew: () => void;\n className?: string;\n}\n\n/**\n * Format the conversation timestamp in pt-BR using the short day/month\n * form (e.g. \"17/04\"), which is enough to disambiguate conversations in\n * the sidebar without overflowing the row.\n */\nfunction formatDate(value: Date | string): string {\n const d = value instanceof Date ? value : new Date(value);\n if (Number.isNaN(d.getTime())) return '';\n return d.toLocaleDateString('pt-BR', {\n day: '2-digit',\n month: '2-digit',\n });\n}\n\n/**\n * Sidebar-like drawer listing the student's past conversations. Clicking\n * a row activates it; the trash icon calls `onDelete` — the actual\n * confirmation flow (modal, toast-undo, etc.) is delegated to the\n * consumer so behaviour can be customized per host app.\n */\nexport default function ChatbotConversationList({\n conversations,\n activeConversationId,\n isLoading = false,\n onSelect,\n onDelete,\n onStartNew,\n className,\n}: Readonly<ChatbotConversationListProps>) {\n return (\n <div\n className={cn(\n 'flex h-full flex-col border-r border-background-200 bg-background',\n className\n )}\n >\n <div className=\"p-3\">\n <Button\n variant=\"solid\"\n action=\"primary\"\n size=\"small\"\n onClick={onStartNew}\n iconLeft={<PlusIcon size={14} />}\n className=\"w-full\"\n >\n Nova conversa\n </Button>\n </div>\n\n <div className=\"flex-1 overflow-y-auto px-2 pb-2\">{renderBody()}</div>\n </div>\n );\n\n function renderBody() {\n if (isLoading) {\n return (\n <div className=\"space-y-2 px-2\">\n <SkeletonText />\n <SkeletonText />\n <SkeletonText />\n </div>\n );\n }\n\n if (conversations.length === 0) {\n return (\n <div className=\"mt-4\">\n <EmptyState\n title=\"Sem conversas ainda\"\n description=\"Comece sua primeira conversa com o assistente.\"\n />\n </div>\n );\n }\n\n return (\n <ul className=\"space-y-1\">\n {conversations.map((c) => {\n const isActive = c.id === activeConversationId;\n return (\n <li key={c.id}>\n <div\n className={cn(\n 'group flex items-start gap-2 rounded-md px-2 py-2 transition-colors',\n isActive ? 'bg-primary-50' : 'hover:bg-background-100'\n )}\n >\n <Button\n variant=\"raw\"\n onClick={() => onSelect(c.id)}\n className=\"flex-1 text-left\"\n >\n <Text\n size=\"sm\"\n weight=\"semibold\"\n className=\"truncate text-text-900\"\n >\n {c.title || 'Conversa sem título'}\n </Text>\n <Text size=\"2xs\" className=\"text-text-400\">\n {formatDate(c.lastMessageAt)}\n </Text>\n </Button>\n <IconButton\n size=\"sm\"\n aria-label={`Excluir conversa ${c.title || ''}`.trim()}\n onClick={() => onDelete(c.id)}\n icon={<TrashIcon size={14} />}\n />\n </div>\n </li>\n );\n })}\n </ul>\n );\n }\n}\n"],"mappings":"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","names":["useEffect","useRef","jsx","jsxs","useEffect","useRef","RobotIcon","jsx","jsx","jsxs","RobotIcon","jsx","jsxs","jsx","jsxs","useRef","useEffect","useState","jsx","jsxs","useState","PlusIcon","jsx","jsxs","PlusIcon","Fragment","jsx","jsxs","useRef","useEffect"]}
@@ -6,7 +6,7 @@ var _chunkD4M467R4js = require('./chunk-D4M467R4.js');
6
6
  var _chunkK74NP7Q7js = require('./chunk-K74NP7Q7.js');
7
7
 
8
8
 
9
- var _chunkV2YHMPRRjs = require('./chunk-V2YHMPRR.js');
9
+ var _chunkKJHBQQYJjs = require('./chunk-KJHBQQYJ.js');
10
10
 
11
11
 
12
12
  var _chunk5DY2JMMJjs = require('./chunk-5DY2JMMJ.js');
@@ -15,7 +15,7 @@ var _chunk5DY2JMMJjs = require('./chunk-5DY2JMMJ.js');
15
15
  var _chunk4DJW2PMPjs = require('./chunk-4DJW2PMP.js');
16
16
 
17
17
 
18
- var _chunkSFQVZ4KQjs = require('./chunk-SFQVZ4KQ.js');
18
+ var _chunk2ETPIOCDjs = require('./chunk-2ETPIOCD.js');
19
19
 
20
20
 
21
21
  var _chunkFRU4YZ4Ujs = require('./chunk-FRU4YZ4U.js');
@@ -183,7 +183,7 @@ var ActivityAccordionCard = ({
183
183
  Math.max(0, activity.correctCount / activity.totalCount * 100)
184
184
  ) : 0;
185
185
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
186
- _chunkSFQVZ4KQjs.CardAccordation,
186
+ _chunk2ETPIOCDjs.CardAccordation,
187
187
  {
188
188
  value: activity.id,
189
189
  className: "bg-background rounded-xl border border-border-50",
@@ -358,7 +358,7 @@ var StudentPerformanceDetailsModal = ({
358
358
  return null;
359
359
  }
360
360
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
361
- _chunkV2YHMPRRjs.ReportDetailModal_default,
361
+ _chunkKJHBQQYJjs.ReportDetailModal_default,
362
362
  {
363
363
  isOpen,
364
364
  onClose,
@@ -375,4 +375,4 @@ var StudentPerformanceDetailsModal = ({
375
375
 
376
376
 
377
377
  exports.StudentPerformanceDetailsModal = StudentPerformanceDetailsModal;
378
- //# sourceMappingURL=chunk-5CQVDUDO.js.map
378
+ //# sourceMappingURL=chunk-BUNF5POU.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-5CQVDUDO.js","../src/components/StudentPerformanceDetailsModal/StudentPerformanceDetailsModal.tsx","../src/components/StudentPerformanceDetailsModal/activityProgress.ts","../src/components/StudentPerformanceDetailsModal/exportSheets.ts"],"names":[],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-5CQVDUDO.js","sourcesContent":[null,"import { useCallback, useMemo, type ReactNode } from 'react';\nimport { UserIcon, TrophyIcon, XCircleIcon } from '@phosphor-icons/react';\nimport ReportDetailModal from '../ReportDetailModal/ReportDetailModal';\nimport Text from '../Text/Text';\nimport Badge from '../Badge/Badge';\nimport ProgressBar from '../ProgressBar/ProgressBar';\nimport { CardAccordation } from '../Accordation/Accordation';\nimport { downloadExcel } from '../../utils/exportExcel';\nimport { formatDateForFileName } from '../../utils/exportFormat';\nimport { formatProgressText, hasActivityData } from './activityProgress';\nimport { buildStudentPerformanceSheets } from './exportSheets';\nimport type {\n StudentPerformanceDetailsModalProps,\n StudentPerformanceDetailsData,\n StudentPerformanceDetailsLabels,\n ActivityProgress,\n} from './types';\nimport { DEFAULT_PERFORMANCE_DETAILS_LABELS } from './types';\nimport { MetricBox } from '../shared/MetricBox';\n\n/**\n * Prefixo do arquivo exportado, em PDF e em XLSX. Recebe a data do dia.\n *\n * Sem o nome do estudante de propósito: ele traz acentos, espaços e às vezes\n * pontuação, e um nome de arquivo pede transliteração — que não existe nesta\n * lib e que não vale a pena inventar aqui.\n */\nconst EXPORT_FILE_PREFIX = 'desempenho-estudante';\n\n/**\n * Teto de altura do modal.\n *\n * Vem do `contentClassName=\"max-h-[80vh]\"` que este componente passava ao\n * `Modal` base; o `ReportDetailModal` não repassa `contentClassName`, então o\n * limite migrou para o `<dialog>` pela prop `className`, onde vence o\n * `max-h-[calc(100dvh-2rem)]` padrão no tailwind-merge (pinado em teste). A\n * rolagem interna continua vindo do `Modal`, que já põe `overflow-y-auto` na\n * área de conteúdo.\n */\nconst MODAL_HEIGHT_CLASS = 'max-h-[80vh]';\n\n/**\n * Performance stat card with colored header and white footer\n * Used for NOTA, CORRETAS, INCORRETAS cards\n */\ninterface PerformanceStatCardProps {\n /** Icon to display */\n icon: ReactNode;\n /** Card label (e.g., \"NOTA\") */\n label: string;\n /** Main value (e.g., \"9.0\" or \"8\") */\n value: string | number;\n /** Secondary label (e.g., \"DESEMPENHO\") */\n secondaryLabel: string;\n /** Badge text (e.g., \"Acima da média\") */\n badgeText: string | null;\n /** Color variant */\n variant: 'orange' | 'green' | 'red';\n}\n\nconst STAT_CARD_COLORS = {\n orange: {\n headerBg: 'bg-warning-background',\n iconBg: 'bg-warning-300',\n valueColor: 'text-warning-600',\n secondaryLabelColor: 'text-warning-500',\n },\n green: {\n headerBg: 'bg-success-200',\n iconBg: 'bg-success-300',\n valueColor: 'text-success-700',\n secondaryLabelColor: 'text-success-600',\n },\n red: {\n headerBg: 'bg-error-100',\n iconBg: 'bg-error-500',\n valueColor: 'text-error-700',\n secondaryLabelColor: 'text-error-300',\n },\n default: {\n headerBg: 'bg-warning-background',\n iconBg: 'bg-warning-300',\n valueColor: 'text-warning-600',\n secondaryLabelColor: 'text-warning-500',\n },\n} as const;\n\n/**\n * Performance stat card component\n */\nconst PerformanceStatCard = ({\n icon,\n label,\n value,\n secondaryLabel,\n badgeText,\n variant,\n}: PerformanceStatCardProps) => {\n const colors = STAT_CARD_COLORS[variant] ?? STAT_CARD_COLORS.default;\n\n return (\n <div className=\"flex flex-col rounded-xl border border-border-50 bg-background overflow-hidden flex-1\">\n {/* Header section with colored background */}\n <div\n className={`flex flex-col items-center justify-center px-2 sm:px-3 py-2 gap-1 ${colors.headerBg}`}\n >\n <Text\n as=\"span\"\n className={`size-8 rounded-full flex items-center justify-center ${colors.iconBg}`}\n >\n {icon}\n </Text>\n <Text\n size=\"2xs\"\n weight=\"medium\"\n className=\"text-text-800 uppercase text-center leading-none\"\n >\n {label}\n </Text>\n <Text\n size=\"xl\"\n weight=\"bold\"\n className={`${colors.valueColor} text-center`}\n >\n {value}\n </Text>\n </div>\n\n {/* Footer section with white background */}\n <div className=\"flex flex-col items-center gap-2 px-2 sm:px-3 py-2\">\n <Text\n size=\"2xs\"\n weight=\"medium\"\n className={`${colors.secondaryLabelColor} uppercase text-center`}\n >\n {secondaryLabel}\n </Text>\n <Badge size=\"large\" action=\"info\">\n {badgeText || '-'}\n </Badge>\n </div>\n </div>\n );\n};\n\n/**\n * Activity progress card with expandable accordion content\n */\ninterface ActivityCardProps {\n /** Activity data */\n activity: ActivityProgress;\n /** No data message */\n noDataMessage: string;\n /** Activity progress text template */\n activityProgressText: string;\n /** Activity details unavailable message */\n activityDetailsUnavailable: string;\n}\n\nconst ActivityAccordionCard = ({\n activity,\n noDataMessage,\n activityProgressText,\n activityDetailsUnavailable,\n}: ActivityCardProps) => {\n // `hasActivityData` e `formatProgressText` vivem em `./activityProgress` para\n // que a aba \"Desempenho atividades\" da planilha decida e formate pelas mesmas\n // regras deste card, em vez de manter uma segunda cópia que pode divergir.\n const hasData = hasActivityData(activity);\n const progressPercentage = hasData\n ? Math.min(\n 100,\n Math.max(0, (activity.correctCount / activity.totalCount) * 100)\n )\n : 0;\n\n return (\n <CardAccordation\n value={activity.id}\n className=\"bg-background rounded-xl border border-border-50\"\n triggerClassName=\"p-4\"\n contentClassName=\"px-4 pb-4\"\n trigger={\n <div className=\"flex flex-col gap-2 flex-1\">\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n {activity.name}\n </Text>\n {hasData ? (\n <div className=\"flex flex-row items-center gap-2\">\n <div className=\"flex-1\">\n <ProgressBar\n value={progressPercentage}\n variant=\"green\"\n size=\"medium\"\n />\n </div>\n <Text\n size=\"xs\"\n weight=\"medium\"\n className=\"text-text-950 whitespace-nowrap\"\n >\n {formatProgressText(\n activityProgressText,\n activity.correctCount,\n activity.totalCount\n )}\n </Text>\n </div>\n ) : (\n <Text size=\"xs\" className=\"text-text-500\">\n {noDataMessage}\n </Text>\n )}\n </div>\n }\n >\n <Text size=\"sm\" className=\"text-text-700\">\n {activity.description || activityDetailsUnavailable}\n </Text>\n </CardAccordation>\n );\n};\n\n/**\n * Loading skeleton for the modal content\n */\nconst LoadingSkeleton = () => (\n <div className=\"flex flex-col gap-4 animate-pulse\">\n <div className=\"h-6 bg-background-200 rounded w-48\" />\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-2\">\n <div className=\"h-48 bg-background-200 rounded-xl\" />\n <div className=\"h-48 bg-background-200 rounded-xl\" />\n <div className=\"h-48 bg-background-200 rounded-xl\" />\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-2\">\n <div className=\"h-20 bg-background-200 rounded-xl\" />\n <div className=\"h-20 bg-background-200 rounded-xl\" />\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-2\">\n <div className=\"h-20 bg-background-200 rounded-xl\" />\n <div className=\"h-20 bg-background-200 rounded-xl\" />\n <div className=\"h-20 bg-background-200 rounded-xl\" />\n </div>\n </div>\n);\n\n/**\n * Error state component\n */\nconst ErrorContent = ({ message }: { message: string }) => (\n <div className=\"flex flex-col items-center justify-center py-8 gap-3\">\n <Text\n as=\"span\"\n className=\"size-12 rounded-full bg-error-100 flex items-center justify-center\"\n >\n <XCircleIcon size={24} className=\"text-error-700\" weight=\"fill\" />\n </Text>\n <Text size=\"md\" className=\"text-error-700 text-center\">\n {message}\n </Text>\n </div>\n);\n\n/**\n * Performance content when data is available\n */\nconst PerformanceContent = ({\n data,\n labels,\n}: {\n data: StudentPerformanceDetailsData;\n labels: StudentPerformanceDetailsLabels;\n}) => (\n <div className=\"flex flex-col gap-4\">\n {/* Student name with icon */}\n <div className=\"flex items-center gap-2 pt-2\">\n <Text\n as=\"span\"\n className=\"size-6 rounded-full bg-primary-100 flex items-center justify-center\"\n >\n <UserIcon size={14} className=\"text-primary-800\" weight=\"fill\" />\n </Text>\n <Text size=\"md\" className=\"text-text-950\">\n {data.studentName}\n </Text>\n </div>\n\n {/* Main stats row - 3 colored cards */}\n <div className=\"flex flex-col sm:flex-row gap-2 sm:gap-3\">\n {/* NOTA card */}\n <PerformanceStatCard\n icon={<TrophyIcon size={16} weight=\"fill\" className=\"text-white\" />}\n label={labels.gradeLabel}\n value={data.grade.value.toFixed(1)}\n secondaryLabel={labels.performanceTagLabel}\n badgeText={data.grade.performanceLabel}\n variant=\"orange\"\n />\n\n {/* CORRETAS card */}\n <PerformanceStatCard\n icon={<TrophyIcon size={16} weight=\"fill\" className=\"text-text-950\" />}\n label={labels.correctQuestionsLabel}\n value={data.correctQuestions.value}\n secondaryLabel={labels.bestResultLabel}\n badgeText={data.correctQuestions.bestResultTopic}\n variant=\"green\"\n />\n\n {/* INCORRETAS card */}\n <PerformanceStatCard\n icon={<XCircleIcon size={16} weight=\"fill\" className=\"text-white\" />}\n label={labels.incorrectQuestionsLabel}\n value={data.incorrectQuestions.value}\n secondaryLabel={labels.hardestTopicLabel}\n badgeText={data.incorrectQuestions.hardestTopic}\n variant=\"red\"\n />\n </div>\n\n {/* Secondary stats row */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-2 sm:gap-3\">\n <MetricBox\n label={labels.activitiesLabel}\n value={data.activitiesCompleted}\n />\n <MetricBox label={labels.questionsLabel} value={data.questionsAnswered} />\n </div>\n\n {/* Tertiary stats row */}\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-2 sm:gap-3\">\n <MetricBox label={labels.accessCountLabel} value={data.accessCount} />\n <MetricBox label={labels.timeOnlineLabel} value={data.timeOnline} />\n <MetricBox label={labels.lastLoginLabel} value={data.lastLogin} />\n </div>\n\n {/* Activities progress section */}\n {data.activities.length > 0 && (\n <div className=\"flex flex-col gap-4 pt-4\">\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n {labels.activitiesProgressTitle}\n </Text>\n <div className=\"flex flex-col gap-2\">\n {data.activities.map((activity) => (\n <ActivityAccordionCard\n key={activity.id}\n activity={activity}\n noDataMessage={labels.noDataMessage}\n activityProgressText={labels.activityProgressText}\n activityDetailsUnavailable={labels.activityDetailsUnavailable}\n />\n ))}\n </div>\n </div>\n )}\n </div>\n);\n\n/**\n * Renders the modal content based on loading, error, and data state\n */\nconst renderModalContent = (\n loading: boolean,\n error: string | null | undefined,\n data: StudentPerformanceDetailsData | null,\n labels: StudentPerformanceDetailsLabels\n): ReactNode => {\n if (loading) {\n return <LoadingSkeleton />;\n }\n\n if (error) {\n return <ErrorContent message={error} />;\n }\n\n if (data) {\n return <PerformanceContent data={data} labels={labels} />;\n }\n\n return null;\n};\n\n/**\n * StudentPerformanceDetailsModal component\n *\n * Displays a modal with detailed student performance information including:\n * - Student name with profile icon\n * - Three main stat cards (grade, correct questions, incorrect questions)\n * - Five metric cards (activities, questions, access, time, last login)\n * - Expandable list of activity progress\n *\n * Exportável: monta sobre o `ReportDetailModal`, então traz o botão \"Baixar\n * relatório\" com PDF (impressão só deste modal) e XLSX. As duas saídas usam o\n * dado que chega por `data` — nada é buscado aqui — e a planilha espelha os\n * blocos acima aba a aba (veja `exportSheets.ts`).\n *\n * O consumidor precisa importar `analytica-frontend-lib/print.css`: sem as\n * regras `@media print` o PDF sai com a página inteira em vez do modal.\n *\n * @example\n * ```tsx\n * <StudentPerformanceDetailsModal\n * isOpen={isModalOpen}\n * onClose={() => setIsModalOpen(false)}\n * data={{\n * studentName: 'Fernanda Rocha',\n * grade: { value: 9.0, performanceLabel: 'Acima da média' },\n * correctQuestions: { value: 8, bestResultTopic: 'Fotossíntese' },\n * incorrectQuestions: { value: 7, hardestTopic: 'Células' },\n * activitiesCompleted: 10,\n * questionsAnswered: 40,\n * accessCount: '00',\n * timeOnline: '00:00:00',\n * lastLogin: '00/00/0000 • 00:00h',\n * activities: [\n * { id: '1', name: 'Atividade 1', correctCount: 30, totalCount: 50 },\n * { id: '2', name: 'Atividade 2', correctCount: 0, totalCount: 0, hasNoData: true },\n * ],\n * }}\n * />\n * ```\n */\nexport const StudentPerformanceDetailsModal = ({\n isOpen,\n onClose,\n data,\n loading = false,\n error = null,\n labels: customLabels,\n}: StudentPerformanceDetailsModalProps) => {\n const labels = useMemo(\n () => ({ ...DEFAULT_PERFORMANCE_DETAILS_LABELS, ...customLabels }),\n [customLabels]\n );\n\n const fileName = `${EXPORT_FILE_PREFIX}-${formatDateForFileName(new Date())}`;\n\n const handleDownloadExcel = useCallback(() => {\n downloadExcel(fileName, buildStudentPerformanceSheets(data, labels));\n }, [fileName, data, labels]);\n\n if (!data && !loading && !error) {\n return null;\n }\n\n return (\n <ReportDetailModal\n isOpen={isOpen}\n onClose={onClose}\n title={labels.title}\n size=\"lg\"\n className={MODAL_HEIGHT_CLASS}\n // Só `fileName`: o PDF é a impressão deste modal, e o\n // `ReportDetailModal` já a faz. Ligar `onDownloadPdf` a um\n // `useReportPrint` local somaria uma segunda impressão — o callback\n // SUBSTITUI a embutida, então o usuário veria dois diálogos.\n fileName={fileName}\n onDownloadExcel={handleDownloadExcel}\n >\n {renderModalContent(loading, error, data, labels)}\n </ReportDetailModal>\n );\n};\n\nexport default StudentPerformanceDetailsModal;\n","import type { ActivityProgress } from './types';\n\n/**\n * Regras de leitura de uma atividade, compartilhadas pela TELA e pela\n * EXPORTAÇÃO.\n *\n * Moram aqui, e não dentro do componente, porque a planilha tem que dizer o\n * mesmo que o card: se as duas tivessem cópias da mesma condição, um ajuste na\n * tela sairia calado do arquivo exportado. Puras, sem React.\n */\n\n/**\n * Uma atividade tem resultado a mostrar?\n *\n * `totalCount > 0` entra junto com a flag porque uma atividade com zero questões\n * dividiria por zero na barra de progresso, e \"0 de 0 corretas\" não informa\n * nada — a tela mostra a mensagem de \"sem dados\" nos dois casos.\n */\nexport const hasActivityData = (activity: ActivityProgress): boolean =>\n !activity.hasNoData && activity.totalCount > 0;\n\n/**\n * Preenche `{correct}` e `{total}` no texto de progresso configurado.\n */\nexport const formatProgressText = (\n template: string,\n correct: number,\n total: number\n): string =>\n template\n .replace('{correct}', String(correct))\n .replace('{total}', String(total));\n","import type { ExcelCell, SheetConfig } from '../../utils/exportExcel';\nimport { formatProgressText, hasActivityData } from './activityProgress';\nimport type {\n StudentPerformanceDetailsData,\n StudentPerformanceDetailsLabels,\n} from './types';\n\n/**\n * Montagem das abas do XLSX do `StudentPerformanceDetailsModal`. Funções puras:\n * nada de React, nada de requisição, nada de escrita em disco — quem grava o\n * arquivo é `downloadExcel`.\n *\n * REGRA QUE GOVERNA ESTE ARQUIVO: a planilha traz o que o modal DESENHA, nada\n * mais e nada menos. As abas espelham os blocos do componente:\n *\n * | Bloco na tela | Onde foi parar |\n * | ------------------------------------------------- | -------------------------- |\n * | nome do estudante (linha com o ícone de usuário) | 1ª linha do resumo |\n * | 3 cards coloridos (NOTA / CORRETAS / INCORRETAS) | resumo, valor + badge |\n * | 2 MetricBox (atividades, questões) | resumo |\n * | 3 MetricBox (acessos, tempo online, último login) | resumo |\n * | lista de atividades (accordion) | aba \"Desempenho atividades\"|\n *\n * O tipo `StudentPerformanceDetailsData` carrega MAIS do que isso:\n * `contentsCompleted` está lá, marcado como deprecated, e o componente nunca o\n * renderiza. Ele fica de fora de propósito — mapear pelo tipo, e não pelo que a\n * tela desenha, poria na planilha um número que o professor nunca viu. Vale para\n * qualquer campo que venha a ser acrescentado ao tipo: só entra aqui depois de\n * entrar na tela.\n */\n\n/**\n * Nome da aba de indicadores. Sem rótulo próprio na tela — o título do modal é\n * do consumidor (`labels.title`, hoje \"Desempenho em 7 dias\") e varia por\n * período, o que daria um nome de aba instável e sem teto de tamanho.\n *\n * 23 caracteres, dentro do limite de 31 do Excel (acima disso o arquivo INTEIRO\n * não abre), e sem nenhum dos caracteres que o formato proíbe (`: \\ / ? * [ ]`).\n */\nexport const SUMMARY_SHEET_NAME = 'Desempenho do estudante';\n\n/**\n * Nome da aba de atividades: 21 caracteres.\n *\n * Repete a palavra do título da seção na tela, mas é um literal e não\n * `labels.activitiesProgressTitle` — um rótulo customizado pelo consumidor\n * poderia passar dos 31 caracteres e derrubar a abertura do arquivo.\n */\nexport const ACTIVITIES_SHEET_NAME = 'Desempenho atividades';\n\n/** O que os badges da tela mostram quando o tópico vem nulo. */\nconst EMPTY_BADGE = '-';\n\n/**\n * Rótulo da linha do nome do estudante.\n *\n * Único texto da planilha que não está na tela: lá o nome aparece ao lado de um\n * ícone de usuário, sem rótulo, e uma tabela de chave/valor precisa de chave.\n */\nconst STUDENT_ROW_LABEL = 'Estudante';\n\nconst SUMMARY_HEADERS = ['Indicador', 'Valor'];\nconst ACTIVITIES_HEADERS = ['Atividade', 'Progresso', 'Descrição'];\n\n/**\n * Aba de indicadores: uma linha por rótulo/valor visível, na ordem da tela.\n *\n * Os rótulos saem do objeto `labels` já resolvido pelo componente, então o que\n * o consumidor customizou na tela aparece igual na planilha.\n *\n * Os valores numéricos vão como número, não como o texto formatado do card:\n * `9` e não `\"9.0\"`. É a única diferença deliberada em relação ao pixel — o\n * `.toFixed(1)` é formatação de exibição, e uma célula numérica é o que permite\n * ordenar e somar do outro lado. O dado é o mesmo.\n */\nexport function buildSummarySheet(\n data: StudentPerformanceDetailsData | null,\n labels: StudentPerformanceDetailsLabels\n): SheetConfig {\n if (!data) {\n return { name: SUMMARY_SHEET_NAME, headers: SUMMARY_HEADERS, rows: [] };\n }\n\n const rows: ExcelCell[][] = [\n [STUDENT_ROW_LABEL, data.studentName],\n [labels.gradeLabel, data.grade.value],\n [labels.performanceTagLabel, data.grade.performanceLabel || EMPTY_BADGE],\n [labels.correctQuestionsLabel, data.correctQuestions.value],\n [\n labels.bestResultLabel,\n data.correctQuestions.bestResultTopic || EMPTY_BADGE,\n ],\n [labels.incorrectQuestionsLabel, data.incorrectQuestions.value],\n [\n labels.hardestTopicLabel,\n data.incorrectQuestions.hardestTopic || EMPTY_BADGE,\n ],\n [labels.activitiesLabel, data.activitiesCompleted],\n [labels.questionsLabel, data.questionsAnswered],\n [labels.accessCountLabel, data.accessCount],\n [labels.timeOnlineLabel, data.timeOnline],\n [labels.lastLoginLabel, data.lastLogin],\n ];\n\n return { name: SUMMARY_SHEET_NAME, headers: SUMMARY_HEADERS, rows };\n}\n\n/**\n * Aba de atividades: uma linha por card do accordion.\n *\n * \"Progresso\" repete o texto do card — o de progresso quando há dados, a\n * mensagem de \"sem dados\" quando não há — usando as MESMAS funções que o\n * componente usa para decidir e formatar. \"Descrição\" é o conteúdo que o\n * accordion revela ao abrir, com o mesmo fallback.\n *\n * A aba existe mesmo com a lista vazia, embora a tela esconda a seção nesse\n * caso: uma aba só com cabeçalho diz \"não havia atividades\", enquanto um\n * conjunto de abas que muda de tamanho conforme o dado quebra quem consome o\n * arquivo.\n */\nexport function buildActivitiesSheet(\n data: StudentPerformanceDetailsData | null,\n labels: StudentPerformanceDetailsLabels\n): SheetConfig {\n const rows: ExcelCell[][] = (data?.activities ?? []).map((activity) => [\n activity.name,\n hasActivityData(activity)\n ? formatProgressText(\n labels.activityProgressText,\n activity.correctCount,\n activity.totalCount\n )\n : labels.noDataMessage,\n activity.description || labels.activityDetailsUnavailable,\n ]);\n\n return { name: ACTIVITIES_SHEET_NAME, headers: ACTIVITIES_HEADERS, rows };\n}\n\n/**\n * As abas da planilha deste modal, na ordem em que os blocos aparecem na tela.\n */\nexport function buildStudentPerformanceSheets(\n data: StudentPerformanceDetailsData | null,\n labels: StudentPerformanceDetailsLabels\n): SheetConfig[] {\n return [buildSummarySheet(data, labels), buildActivitiesSheet(data, labels)];\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-BUNF5POU.js","../src/components/StudentPerformanceDetailsModal/StudentPerformanceDetailsModal.tsx","../src/components/StudentPerformanceDetailsModal/activityProgress.ts","../src/components/StudentPerformanceDetailsModal/exportSheets.ts"],"names":[],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-BUNF5POU.js","sourcesContent":[null,"import { useCallback, useMemo, type ReactNode } from 'react';\nimport { UserIcon, TrophyIcon, XCircleIcon } from '@phosphor-icons/react';\nimport ReportDetailModal from '../ReportDetailModal/ReportDetailModal';\nimport Text from '../Text/Text';\nimport Badge from '../Badge/Badge';\nimport ProgressBar from '../ProgressBar/ProgressBar';\nimport { CardAccordation } from '../Accordation/Accordation';\nimport { downloadExcel } from '../../utils/exportExcel';\nimport { formatDateForFileName } from '../../utils/exportFormat';\nimport { formatProgressText, hasActivityData } from './activityProgress';\nimport { buildStudentPerformanceSheets } from './exportSheets';\nimport type {\n StudentPerformanceDetailsModalProps,\n StudentPerformanceDetailsData,\n StudentPerformanceDetailsLabels,\n ActivityProgress,\n} from './types';\nimport { DEFAULT_PERFORMANCE_DETAILS_LABELS } from './types';\nimport { MetricBox } from '../shared/MetricBox';\n\n/**\n * Prefixo do arquivo exportado, em PDF e em XLSX. Recebe a data do dia.\n *\n * Sem o nome do estudante de propósito: ele traz acentos, espaços e às vezes\n * pontuação, e um nome de arquivo pede transliteração — que não existe nesta\n * lib e que não vale a pena inventar aqui.\n */\nconst EXPORT_FILE_PREFIX = 'desempenho-estudante';\n\n/**\n * Teto de altura do modal.\n *\n * Vem do `contentClassName=\"max-h-[80vh]\"` que este componente passava ao\n * `Modal` base; o `ReportDetailModal` não repassa `contentClassName`, então o\n * limite migrou para o `<dialog>` pela prop `className`, onde vence o\n * `max-h-[calc(100dvh-2rem)]` padrão no tailwind-merge (pinado em teste). A\n * rolagem interna continua vindo do `Modal`, que já põe `overflow-y-auto` na\n * área de conteúdo.\n */\nconst MODAL_HEIGHT_CLASS = 'max-h-[80vh]';\n\n/**\n * Performance stat card with colored header and white footer\n * Used for NOTA, CORRETAS, INCORRETAS cards\n */\ninterface PerformanceStatCardProps {\n /** Icon to display */\n icon: ReactNode;\n /** Card label (e.g., \"NOTA\") */\n label: string;\n /** Main value (e.g., \"9.0\" or \"8\") */\n value: string | number;\n /** Secondary label (e.g., \"DESEMPENHO\") */\n secondaryLabel: string;\n /** Badge text (e.g., \"Acima da média\") */\n badgeText: string | null;\n /** Color variant */\n variant: 'orange' | 'green' | 'red';\n}\n\nconst STAT_CARD_COLORS = {\n orange: {\n headerBg: 'bg-warning-background',\n iconBg: 'bg-warning-300',\n valueColor: 'text-warning-600',\n secondaryLabelColor: 'text-warning-500',\n },\n green: {\n headerBg: 'bg-success-200',\n iconBg: 'bg-success-300',\n valueColor: 'text-success-700',\n secondaryLabelColor: 'text-success-600',\n },\n red: {\n headerBg: 'bg-error-100',\n iconBg: 'bg-error-500',\n valueColor: 'text-error-700',\n secondaryLabelColor: 'text-error-300',\n },\n default: {\n headerBg: 'bg-warning-background',\n iconBg: 'bg-warning-300',\n valueColor: 'text-warning-600',\n secondaryLabelColor: 'text-warning-500',\n },\n} as const;\n\n/**\n * Performance stat card component\n */\nconst PerformanceStatCard = ({\n icon,\n label,\n value,\n secondaryLabel,\n badgeText,\n variant,\n}: PerformanceStatCardProps) => {\n const colors = STAT_CARD_COLORS[variant] ?? STAT_CARD_COLORS.default;\n\n return (\n <div className=\"flex flex-col rounded-xl border border-border-50 bg-background overflow-hidden flex-1\">\n {/* Header section with colored background */}\n <div\n className={`flex flex-col items-center justify-center px-2 sm:px-3 py-2 gap-1 ${colors.headerBg}`}\n >\n <Text\n as=\"span\"\n className={`size-8 rounded-full flex items-center justify-center ${colors.iconBg}`}\n >\n {icon}\n </Text>\n <Text\n size=\"2xs\"\n weight=\"medium\"\n className=\"text-text-800 uppercase text-center leading-none\"\n >\n {label}\n </Text>\n <Text\n size=\"xl\"\n weight=\"bold\"\n className={`${colors.valueColor} text-center`}\n >\n {value}\n </Text>\n </div>\n\n {/* Footer section with white background */}\n <div className=\"flex flex-col items-center gap-2 px-2 sm:px-3 py-2\">\n <Text\n size=\"2xs\"\n weight=\"medium\"\n className={`${colors.secondaryLabelColor} uppercase text-center`}\n >\n {secondaryLabel}\n </Text>\n <Badge size=\"large\" action=\"info\">\n {badgeText || '-'}\n </Badge>\n </div>\n </div>\n );\n};\n\n/**\n * Activity progress card with expandable accordion content\n */\ninterface ActivityCardProps {\n /** Activity data */\n activity: ActivityProgress;\n /** No data message */\n noDataMessage: string;\n /** Activity progress text template */\n activityProgressText: string;\n /** Activity details unavailable message */\n activityDetailsUnavailable: string;\n}\n\nconst ActivityAccordionCard = ({\n activity,\n noDataMessage,\n activityProgressText,\n activityDetailsUnavailable,\n}: ActivityCardProps) => {\n // `hasActivityData` e `formatProgressText` vivem em `./activityProgress` para\n // que a aba \"Desempenho atividades\" da planilha decida e formate pelas mesmas\n // regras deste card, em vez de manter uma segunda cópia que pode divergir.\n const hasData = hasActivityData(activity);\n const progressPercentage = hasData\n ? Math.min(\n 100,\n Math.max(0, (activity.correctCount / activity.totalCount) * 100)\n )\n : 0;\n\n return (\n <CardAccordation\n value={activity.id}\n className=\"bg-background rounded-xl border border-border-50\"\n triggerClassName=\"p-4\"\n contentClassName=\"px-4 pb-4\"\n trigger={\n <div className=\"flex flex-col gap-2 flex-1\">\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n {activity.name}\n </Text>\n {hasData ? (\n <div className=\"flex flex-row items-center gap-2\">\n <div className=\"flex-1\">\n <ProgressBar\n value={progressPercentage}\n variant=\"green\"\n size=\"medium\"\n />\n </div>\n <Text\n size=\"xs\"\n weight=\"medium\"\n className=\"text-text-950 whitespace-nowrap\"\n >\n {formatProgressText(\n activityProgressText,\n activity.correctCount,\n activity.totalCount\n )}\n </Text>\n </div>\n ) : (\n <Text size=\"xs\" className=\"text-text-500\">\n {noDataMessage}\n </Text>\n )}\n </div>\n }\n >\n <Text size=\"sm\" className=\"text-text-700\">\n {activity.description || activityDetailsUnavailable}\n </Text>\n </CardAccordation>\n );\n};\n\n/**\n * Loading skeleton for the modal content\n */\nconst LoadingSkeleton = () => (\n <div className=\"flex flex-col gap-4 animate-pulse\">\n <div className=\"h-6 bg-background-200 rounded w-48\" />\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-2\">\n <div className=\"h-48 bg-background-200 rounded-xl\" />\n <div className=\"h-48 bg-background-200 rounded-xl\" />\n <div className=\"h-48 bg-background-200 rounded-xl\" />\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-2\">\n <div className=\"h-20 bg-background-200 rounded-xl\" />\n <div className=\"h-20 bg-background-200 rounded-xl\" />\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-2\">\n <div className=\"h-20 bg-background-200 rounded-xl\" />\n <div className=\"h-20 bg-background-200 rounded-xl\" />\n <div className=\"h-20 bg-background-200 rounded-xl\" />\n </div>\n </div>\n);\n\n/**\n * Error state component\n */\nconst ErrorContent = ({ message }: { message: string }) => (\n <div className=\"flex flex-col items-center justify-center py-8 gap-3\">\n <Text\n as=\"span\"\n className=\"size-12 rounded-full bg-error-100 flex items-center justify-center\"\n >\n <XCircleIcon size={24} className=\"text-error-700\" weight=\"fill\" />\n </Text>\n <Text size=\"md\" className=\"text-error-700 text-center\">\n {message}\n </Text>\n </div>\n);\n\n/**\n * Performance content when data is available\n */\nconst PerformanceContent = ({\n data,\n labels,\n}: {\n data: StudentPerformanceDetailsData;\n labels: StudentPerformanceDetailsLabels;\n}) => (\n <div className=\"flex flex-col gap-4\">\n {/* Student name with icon */}\n <div className=\"flex items-center gap-2 pt-2\">\n <Text\n as=\"span\"\n className=\"size-6 rounded-full bg-primary-100 flex items-center justify-center\"\n >\n <UserIcon size={14} className=\"text-primary-800\" weight=\"fill\" />\n </Text>\n <Text size=\"md\" className=\"text-text-950\">\n {data.studentName}\n </Text>\n </div>\n\n {/* Main stats row - 3 colored cards */}\n <div className=\"flex flex-col sm:flex-row gap-2 sm:gap-3\">\n {/* NOTA card */}\n <PerformanceStatCard\n icon={<TrophyIcon size={16} weight=\"fill\" className=\"text-white\" />}\n label={labels.gradeLabel}\n value={data.grade.value.toFixed(1)}\n secondaryLabel={labels.performanceTagLabel}\n badgeText={data.grade.performanceLabel}\n variant=\"orange\"\n />\n\n {/* CORRETAS card */}\n <PerformanceStatCard\n icon={<TrophyIcon size={16} weight=\"fill\" className=\"text-text-950\" />}\n label={labels.correctQuestionsLabel}\n value={data.correctQuestions.value}\n secondaryLabel={labels.bestResultLabel}\n badgeText={data.correctQuestions.bestResultTopic}\n variant=\"green\"\n />\n\n {/* INCORRETAS card */}\n <PerformanceStatCard\n icon={<XCircleIcon size={16} weight=\"fill\" className=\"text-white\" />}\n label={labels.incorrectQuestionsLabel}\n value={data.incorrectQuestions.value}\n secondaryLabel={labels.hardestTopicLabel}\n badgeText={data.incorrectQuestions.hardestTopic}\n variant=\"red\"\n />\n </div>\n\n {/* Secondary stats row */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-2 sm:gap-3\">\n <MetricBox\n label={labels.activitiesLabel}\n value={data.activitiesCompleted}\n />\n <MetricBox label={labels.questionsLabel} value={data.questionsAnswered} />\n </div>\n\n {/* Tertiary stats row */}\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-2 sm:gap-3\">\n <MetricBox label={labels.accessCountLabel} value={data.accessCount} />\n <MetricBox label={labels.timeOnlineLabel} value={data.timeOnline} />\n <MetricBox label={labels.lastLoginLabel} value={data.lastLogin} />\n </div>\n\n {/* Activities progress section */}\n {data.activities.length > 0 && (\n <div className=\"flex flex-col gap-4 pt-4\">\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n {labels.activitiesProgressTitle}\n </Text>\n <div className=\"flex flex-col gap-2\">\n {data.activities.map((activity) => (\n <ActivityAccordionCard\n key={activity.id}\n activity={activity}\n noDataMessage={labels.noDataMessage}\n activityProgressText={labels.activityProgressText}\n activityDetailsUnavailable={labels.activityDetailsUnavailable}\n />\n ))}\n </div>\n </div>\n )}\n </div>\n);\n\n/**\n * Renders the modal content based on loading, error, and data state\n */\nconst renderModalContent = (\n loading: boolean,\n error: string | null | undefined,\n data: StudentPerformanceDetailsData | null,\n labels: StudentPerformanceDetailsLabels\n): ReactNode => {\n if (loading) {\n return <LoadingSkeleton />;\n }\n\n if (error) {\n return <ErrorContent message={error} />;\n }\n\n if (data) {\n return <PerformanceContent data={data} labels={labels} />;\n }\n\n return null;\n};\n\n/**\n * StudentPerformanceDetailsModal component\n *\n * Displays a modal with detailed student performance information including:\n * - Student name with profile icon\n * - Three main stat cards (grade, correct questions, incorrect questions)\n * - Five metric cards (activities, questions, access, time, last login)\n * - Expandable list of activity progress\n *\n * Exportável: monta sobre o `ReportDetailModal`, então traz o botão \"Baixar\n * relatório\" com PDF (impressão só deste modal) e XLSX. As duas saídas usam o\n * dado que chega por `data` — nada é buscado aqui — e a planilha espelha os\n * blocos acima aba a aba (veja `exportSheets.ts`).\n *\n * O consumidor precisa importar `analytica-frontend-lib/print.css`: sem as\n * regras `@media print` o PDF sai com a página inteira em vez do modal.\n *\n * @example\n * ```tsx\n * <StudentPerformanceDetailsModal\n * isOpen={isModalOpen}\n * onClose={() => setIsModalOpen(false)}\n * data={{\n * studentName: 'Fernanda Rocha',\n * grade: { value: 9.0, performanceLabel: 'Acima da média' },\n * correctQuestions: { value: 8, bestResultTopic: 'Fotossíntese' },\n * incorrectQuestions: { value: 7, hardestTopic: 'Células' },\n * activitiesCompleted: 10,\n * questionsAnswered: 40,\n * accessCount: '00',\n * timeOnline: '00:00:00',\n * lastLogin: '00/00/0000 • 00:00h',\n * activities: [\n * { id: '1', name: 'Atividade 1', correctCount: 30, totalCount: 50 },\n * { id: '2', name: 'Atividade 2', correctCount: 0, totalCount: 0, hasNoData: true },\n * ],\n * }}\n * />\n * ```\n */\nexport const StudentPerformanceDetailsModal = ({\n isOpen,\n onClose,\n data,\n loading = false,\n error = null,\n labels: customLabels,\n}: StudentPerformanceDetailsModalProps) => {\n const labels = useMemo(\n () => ({ ...DEFAULT_PERFORMANCE_DETAILS_LABELS, ...customLabels }),\n [customLabels]\n );\n\n const fileName = `${EXPORT_FILE_PREFIX}-${formatDateForFileName(new Date())}`;\n\n const handleDownloadExcel = useCallback(() => {\n downloadExcel(fileName, buildStudentPerformanceSheets(data, labels));\n }, [fileName, data, labels]);\n\n if (!data && !loading && !error) {\n return null;\n }\n\n return (\n <ReportDetailModal\n isOpen={isOpen}\n onClose={onClose}\n title={labels.title}\n size=\"lg\"\n className={MODAL_HEIGHT_CLASS}\n // Só `fileName`: o PDF é a impressão deste modal, e o\n // `ReportDetailModal` já a faz. Ligar `onDownloadPdf` a um\n // `useReportPrint` local somaria uma segunda impressão — o callback\n // SUBSTITUI a embutida, então o usuário veria dois diálogos.\n fileName={fileName}\n onDownloadExcel={handleDownloadExcel}\n >\n {renderModalContent(loading, error, data, labels)}\n </ReportDetailModal>\n );\n};\n\nexport default StudentPerformanceDetailsModal;\n","import type { ActivityProgress } from './types';\n\n/**\n * Regras de leitura de uma atividade, compartilhadas pela TELA e pela\n * EXPORTAÇÃO.\n *\n * Moram aqui, e não dentro do componente, porque a planilha tem que dizer o\n * mesmo que o card: se as duas tivessem cópias da mesma condição, um ajuste na\n * tela sairia calado do arquivo exportado. Puras, sem React.\n */\n\n/**\n * Uma atividade tem resultado a mostrar?\n *\n * `totalCount > 0` entra junto com a flag porque uma atividade com zero questões\n * dividiria por zero na barra de progresso, e \"0 de 0 corretas\" não informa\n * nada — a tela mostra a mensagem de \"sem dados\" nos dois casos.\n */\nexport const hasActivityData = (activity: ActivityProgress): boolean =>\n !activity.hasNoData && activity.totalCount > 0;\n\n/**\n * Preenche `{correct}` e `{total}` no texto de progresso configurado.\n */\nexport const formatProgressText = (\n template: string,\n correct: number,\n total: number\n): string =>\n template\n .replace('{correct}', String(correct))\n .replace('{total}', String(total));\n","import type { ExcelCell, SheetConfig } from '../../utils/exportExcel';\nimport { formatProgressText, hasActivityData } from './activityProgress';\nimport type {\n StudentPerformanceDetailsData,\n StudentPerformanceDetailsLabels,\n} from './types';\n\n/**\n * Montagem das abas do XLSX do `StudentPerformanceDetailsModal`. Funções puras:\n * nada de React, nada de requisição, nada de escrita em disco — quem grava o\n * arquivo é `downloadExcel`.\n *\n * REGRA QUE GOVERNA ESTE ARQUIVO: a planilha traz o que o modal DESENHA, nada\n * mais e nada menos. As abas espelham os blocos do componente:\n *\n * | Bloco na tela | Onde foi parar |\n * | ------------------------------------------------- | -------------------------- |\n * | nome do estudante (linha com o ícone de usuário) | 1ª linha do resumo |\n * | 3 cards coloridos (NOTA / CORRETAS / INCORRETAS) | resumo, valor + badge |\n * | 2 MetricBox (atividades, questões) | resumo |\n * | 3 MetricBox (acessos, tempo online, último login) | resumo |\n * | lista de atividades (accordion) | aba \"Desempenho atividades\"|\n *\n * O tipo `StudentPerformanceDetailsData` carrega MAIS do que isso:\n * `contentsCompleted` está lá, marcado como deprecated, e o componente nunca o\n * renderiza. Ele fica de fora de propósito — mapear pelo tipo, e não pelo que a\n * tela desenha, poria na planilha um número que o professor nunca viu. Vale para\n * qualquer campo que venha a ser acrescentado ao tipo: só entra aqui depois de\n * entrar na tela.\n */\n\n/**\n * Nome da aba de indicadores. Sem rótulo próprio na tela — o título do modal é\n * do consumidor (`labels.title`, hoje \"Desempenho em 7 dias\") e varia por\n * período, o que daria um nome de aba instável e sem teto de tamanho.\n *\n * 23 caracteres, dentro do limite de 31 do Excel (acima disso o arquivo INTEIRO\n * não abre), e sem nenhum dos caracteres que o formato proíbe (`: \\ / ? * [ ]`).\n */\nexport const SUMMARY_SHEET_NAME = 'Desempenho do estudante';\n\n/**\n * Nome da aba de atividades: 21 caracteres.\n *\n * Repete a palavra do título da seção na tela, mas é um literal e não\n * `labels.activitiesProgressTitle` — um rótulo customizado pelo consumidor\n * poderia passar dos 31 caracteres e derrubar a abertura do arquivo.\n */\nexport const ACTIVITIES_SHEET_NAME = 'Desempenho atividades';\n\n/** O que os badges da tela mostram quando o tópico vem nulo. */\nconst EMPTY_BADGE = '-';\n\n/**\n * Rótulo da linha do nome do estudante.\n *\n * Único texto da planilha que não está na tela: lá o nome aparece ao lado de um\n * ícone de usuário, sem rótulo, e uma tabela de chave/valor precisa de chave.\n */\nconst STUDENT_ROW_LABEL = 'Estudante';\n\nconst SUMMARY_HEADERS = ['Indicador', 'Valor'];\nconst ACTIVITIES_HEADERS = ['Atividade', 'Progresso', 'Descrição'];\n\n/**\n * Aba de indicadores: uma linha por rótulo/valor visível, na ordem da tela.\n *\n * Os rótulos saem do objeto `labels` já resolvido pelo componente, então o que\n * o consumidor customizou na tela aparece igual na planilha.\n *\n * Os valores numéricos vão como número, não como o texto formatado do card:\n * `9` e não `\"9.0\"`. É a única diferença deliberada em relação ao pixel — o\n * `.toFixed(1)` é formatação de exibição, e uma célula numérica é o que permite\n * ordenar e somar do outro lado. O dado é o mesmo.\n */\nexport function buildSummarySheet(\n data: StudentPerformanceDetailsData | null,\n labels: StudentPerformanceDetailsLabels\n): SheetConfig {\n if (!data) {\n return { name: SUMMARY_SHEET_NAME, headers: SUMMARY_HEADERS, rows: [] };\n }\n\n const rows: ExcelCell[][] = [\n [STUDENT_ROW_LABEL, data.studentName],\n [labels.gradeLabel, data.grade.value],\n [labels.performanceTagLabel, data.grade.performanceLabel || EMPTY_BADGE],\n [labels.correctQuestionsLabel, data.correctQuestions.value],\n [\n labels.bestResultLabel,\n data.correctQuestions.bestResultTopic || EMPTY_BADGE,\n ],\n [labels.incorrectQuestionsLabel, data.incorrectQuestions.value],\n [\n labels.hardestTopicLabel,\n data.incorrectQuestions.hardestTopic || EMPTY_BADGE,\n ],\n [labels.activitiesLabel, data.activitiesCompleted],\n [labels.questionsLabel, data.questionsAnswered],\n [labels.accessCountLabel, data.accessCount],\n [labels.timeOnlineLabel, data.timeOnline],\n [labels.lastLoginLabel, data.lastLogin],\n ];\n\n return { name: SUMMARY_SHEET_NAME, headers: SUMMARY_HEADERS, rows };\n}\n\n/**\n * Aba de atividades: uma linha por card do accordion.\n *\n * \"Progresso\" repete o texto do card — o de progresso quando há dados, a\n * mensagem de \"sem dados\" quando não há — usando as MESMAS funções que o\n * componente usa para decidir e formatar. \"Descrição\" é o conteúdo que o\n * accordion revela ao abrir, com o mesmo fallback.\n *\n * A aba existe mesmo com a lista vazia, embora a tela esconda a seção nesse\n * caso: uma aba só com cabeçalho diz \"não havia atividades\", enquanto um\n * conjunto de abas que muda de tamanho conforme o dado quebra quem consome o\n * arquivo.\n */\nexport function buildActivitiesSheet(\n data: StudentPerformanceDetailsData | null,\n labels: StudentPerformanceDetailsLabels\n): SheetConfig {\n const rows: ExcelCell[][] = (data?.activities ?? []).map((activity) => [\n activity.name,\n hasActivityData(activity)\n ? formatProgressText(\n labels.activityProgressText,\n activity.correctCount,\n activity.totalCount\n )\n : labels.noDataMessage,\n activity.description || labels.activityDetailsUnavailable,\n ]);\n\n return { name: ACTIVITIES_SHEET_NAME, headers: ACTIVITIES_HEADERS, rows };\n}\n\n/**\n * As abas da planilha deste modal, na ordem em que os blocos aparecem na tela.\n */\nexport function buildStudentPerformanceSheets(\n data: StudentPerformanceDetailsData | null,\n labels: StudentPerformanceDetailsLabels\n): SheetConfig[] {\n return [buildSummarySheet(data, labels), buildActivitiesSheet(data, labels)];\n}\n"]}
@@ -3,15 +3,15 @@ import {
3
3
  } from "./chunk-IG4GK7ME.mjs";
4
4
  import {
5
5
  IconButton_default
6
- } from "./chunk-LGQNMQOT.mjs";
6
+ } from "./chunk-ROS5XGTQ.mjs";
7
7
  import {
8
8
  DropdownMenuContent,
9
9
  DropdownMenuTrigger,
10
10
  DropdownMenu_default
11
- } from "./chunk-WDPFO4II.mjs";
11
+ } from "./chunk-NMPK336K.mjs";
12
12
  import {
13
13
  Modal_default
14
- } from "./chunk-MUCV5ZWZ.mjs";
14
+ } from "./chunk-3YF7H577.mjs";
15
15
 
16
16
  // src/components/CalendarCard/CalendarCard.tsx
17
17
  import { CalendarBlankIcon } from "@phosphor-icons/react/dist/csr/CalendarBlank";
@@ -98,4 +98,4 @@ export {
98
98
  CalendarCard,
99
99
  CalendarCard_default
100
100
  };
101
- //# sourceMappingURL=chunk-Z6UN4ZPW.mjs.map
101
+ //# sourceMappingURL=chunk-BVHVYC7Q.mjs.map
@@ -14,7 +14,7 @@ var _chunkBJMKBBN3js = require('./chunk-BJMKBBN3.js');
14
14
  var _chunkWWXDXIV7js = require('./chunk-WWXDXIV7.js');
15
15
 
16
16
 
17
- var _chunkG4NK66PMjs = require('./chunk-G4NK66PM.js');
17
+ var _chunkJTWXQ267js = require('./chunk-JTWXQ267.js');
18
18
 
19
19
 
20
20
  var _chunkXN2HPVVHjs = require('./chunk-XN2HPVVH.js');
@@ -57,7 +57,7 @@ var AlertsManagerView = ({
57
57
  });
58
58
  };
59
59
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
60
- _chunkG4NK66PMjs.Modal_default,
60
+ _chunkJTWXQ267js.Modal_default,
61
61
  {
62
62
  isOpen,
63
63
  onClose: handleClose,
@@ -160,4 +160,4 @@ var AlertsManagerView = ({
160
160
 
161
161
 
162
162
  exports.AlertsManagerView = AlertsManagerView;
163
- //# sourceMappingURL=chunk-WJENEFDG.js.map
163
+ //# sourceMappingURL=chunk-CAFVLHDV.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-WJENEFDG.js","../src/components/AlertManagerView/AlertsManagerView.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-WJENEFDG.js","sourcesContent":[null,"import Modal from '../Modal/Modal';\nimport Text from '../Text/Text';\nimport Divider from '../Divider/Divider';\nimport Table, {\n TableHeader,\n TableBody,\n TableRow,\n TableHead,\n TableCell,\n} from '../Table/Table';\nimport Button from '../Button/Button';\nimport Badge from '../Badge/Badge';\nimport { CaretLeftIcon } from '@phosphor-icons/react/dist/csr/CaretLeft';\nimport { CaretRightIcon } from '@phosphor-icons/react/dist/csr/CaretRight';\nimport { UserIcon } from '@phosphor-icons/react/dist/csr/User';\nimport type { AlertData } from '../AlertManager/types';\n\n// Interface para os dados de visualização do alerta\nexport interface AlertViewData extends Omit<AlertData, 'image'> {\n image?: string; // No view, a imagem sempre é uma URL (string)\n sentAt: string | Date; // Data de envio formatada\n recipients: RecipientStatus[];\n}\n\n// Interface para o status de cada destinatário\nexport interface RecipientStatus {\n id: string;\n name: string;\n status: 'viewed' | 'pending';\n}\n\ninterface AlertsManagerViewProps {\n alertData: AlertViewData;\n isOpen?: boolean;\n onClose?: () => void;\n /** URL da imagem após upload (prioritária - será exibida primeiro) */\n imageLink?: string | null;\n /** Imagem padrão a ser exibida quando não há imagem no alertData (URL string) */\n defaultImage?: string | null;\n // Controle de paginação\n currentPage?: number;\n totalPages?: number;\n onPageChange?: (page: number) => void;\n itemsPerPage?: number;\n}\n\nexport const AlertsManagerView = ({\n alertData,\n isOpen = false,\n onClose,\n imageLink,\n defaultImage,\n currentPage = 1,\n totalPages: externalTotalPages,\n onPageChange,\n itemsPerPage = 10,\n}: AlertsManagerViewProps) => {\n // Calcular paginação (usar props externas se fornecidas, senão calcular)\n const totalPages =\n externalTotalPages ?? Math.ceil(alertData.recipients.length / itemsPerPage);\n\n // Clamp currentPage to valid range\n const effectiveCurrentPage = Math.min(totalPages, Math.max(1, currentPage));\n\n /**\n * Quando o consumidor informa `totalPages` e `onPageChange`, ele já buscou a\n * página no servidor e `recipients` contém apenas os itens dela. Fatiar de novo\n * usaria um índice de página global sobre um array de tamanho de página: na\n * página 2, `slice(10, 20)` sobre 10 itens devolve `[]` e a tabela de\n * Visualizado/Pendente fica vazia da página 2 em diante.\n *\n * Sem essas props o componente continua no modo client-side, recebendo a lista\n * inteira e fatiando — que é como as stories e os testes o usam.\n */\n const isServerPaginated =\n externalTotalPages !== undefined && onPageChange !== undefined;\n\n const startIndex = (effectiveCurrentPage - 1) * itemsPerPage;\n const endIndex = startIndex + itemsPerPage;\n const paginatedRecipients = isServerPaginated\n ? alertData.recipients\n : alertData.recipients.slice(startIndex, endIndex);\n\n const handleClose = () => {\n onClose?.();\n };\n\n const formatDate = (dateInput: string | Date) => {\n const date = dateInput instanceof Date ? dateInput : new Date(dateInput);\n if (Number.isNaN(date.getTime())) return String(dateInput);\n return date.toLocaleDateString('pt-BR', {\n day: '2-digit',\n month: '2-digit',\n year: 'numeric',\n });\n };\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={handleClose}\n title={alertData.title}\n size=\"md\"\n contentClassName=\"p-0\"\n >\n <div className=\"flex flex-col h-[calc(100vh-8rem)] max-h-[700px]\">\n {/* Área de conteúdo com scroll */}\n <div className=\"flex-1 overflow-y-auto px-6 py-4\">\n {/* Preview do alerta */}\n <div className=\"bg-background-50 px-5 py-6 flex flex-col items-center gap-4 rounded-xl mb-4\">\n {/* Prioridade: imageLink > alertData.image > defaultImage */}\n {(imageLink || alertData.image || defaultImage) && (\n <img\n src={imageLink || alertData.image || defaultImage || undefined}\n alt={alertData.title || 'Imagem do alerta'}\n />\n )}\n <div className=\"flex flex-col items-center text-center gap-3\">\n <Text size=\"lg\" weight=\"semibold\">\n {alertData.title || 'Sem Título'}\n </Text>\n <Text size=\"sm\" weight=\"normal\" className=\"text-text-500\">\n {alertData.message || 'Sem mensagem'}\n </Text>\n </div>\n </div>\n\n {/* Divider */}\n <Divider className=\"my-4\" />\n\n {/* Data de envio */}\n <div className=\"flex justify-between items-center mb-4 px-2\">\n <Text size=\"sm\" weight=\"bold\" className=\"text-text-700\">\n Enviado em\n </Text>\n <Text size=\"sm\" weight=\"medium\" className=\"text-text-900\">\n {formatDate(alertData.sentAt)}\n </Text>\n </div>\n\n <Divider className=\"my-4\" />\n\n {/* Tabela de destinatários */}\n <div className=\"mb-4\">\n <Table variant=\"borderless\" className=\"table-fixed\">\n <TableHeader>\n <TableRow variant=\"borderless\">\n <TableHead className=\"py-2 px-3.5 text-start\">\n Destinatário\n </TableHead>\n <TableHead className=\"py-2 px-3.5 w-[120px] text-start\">\n Status\n </TableHead>\n </TableRow>\n </TableHeader>\n <TableBody variant=\"borderless\">\n {paginatedRecipients.map((recipient) => (\n <TableRow key={recipient.id}>\n <TableCell className=\"py-2 px-3.5 flex flex-row gap-2 text-start truncate\">\n <div className=\"rounded-full size-6 bg-primary-100 flex items-center justify-center\">\n <UserIcon className=\"text-primary-950\" size={18} />\n </div>\n {recipient.name}\n </TableCell>\n <TableCell className=\"py-2 px-3.5 text-center\">\n <div className=\"flex justify-center items-center gap-1\">\n {recipient.status === 'viewed' ? (\n <Badge variant=\"solid\" action=\"success\">\n Visualizado\n </Badge>\n ) : (\n <Badge variant=\"solid\" action=\"error\">\n Pendente\n </Badge>\n )}\n </div>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n\n {/* Paginação */}\n {totalPages > 1 && (\n <div className=\"flex justify-end items-center gap-2 bg-background-50 border border-border-200 py-3.5 px-2 rounded-b-2xl\">\n <Text size=\"sm\" className=\"text-text-600\">\n Página {effectiveCurrentPage} de {totalPages}\n </Text>\n <div className=\"flex gap-2\">\n {onPageChange ? (\n <>\n <Button\n variant=\"link\"\n size=\"extra-small\"\n onClick={() =>\n onPageChange(Math.max(1, effectiveCurrentPage - 1))\n }\n disabled={effectiveCurrentPage === 1}\n iconLeft={<CaretLeftIcon />}\n >\n Anterior\n </Button>\n <Button\n variant=\"link\"\n size=\"extra-small\"\n onClick={() =>\n onPageChange(\n Math.min(totalPages, effectiveCurrentPage + 1)\n )\n }\n disabled={effectiveCurrentPage === totalPages}\n iconRight={<CaretRightIcon />}\n >\n Próximo\n </Button>\n </>\n ) : (\n <>\n <Button\n variant=\"link\"\n size=\"extra-small\"\n disabled={effectiveCurrentPage === 1}\n iconLeft={<CaretLeftIcon />}\n >\n Anterior\n </Button>\n <Button\n variant=\"link\"\n size=\"extra-small\"\n disabled={effectiveCurrentPage === totalPages}\n iconRight={<CaretRightIcon />}\n >\n Próximo\n </Button>\n </>\n )}\n </div>\n </div>\n )}\n </div>\n </div>\n </Modal>\n );\n};\n"]}
1
+ {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-CAFVLHDV.js","../src/components/AlertManagerView/AlertsManagerView.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-CAFVLHDV.js","sourcesContent":[null,"import Modal from '../Modal/Modal';\nimport Text from '../Text/Text';\nimport Divider from '../Divider/Divider';\nimport Table, {\n TableHeader,\n TableBody,\n TableRow,\n TableHead,\n TableCell,\n} from '../Table/Table';\nimport Button from '../Button/Button';\nimport Badge from '../Badge/Badge';\nimport { CaretLeftIcon } from '@phosphor-icons/react/dist/csr/CaretLeft';\nimport { CaretRightIcon } from '@phosphor-icons/react/dist/csr/CaretRight';\nimport { UserIcon } from '@phosphor-icons/react/dist/csr/User';\nimport type { AlertData } from '../AlertManager/types';\n\n// Interface para os dados de visualização do alerta\nexport interface AlertViewData extends Omit<AlertData, 'image'> {\n image?: string; // No view, a imagem sempre é uma URL (string)\n sentAt: string | Date; // Data de envio formatada\n recipients: RecipientStatus[];\n}\n\n// Interface para o status de cada destinatário\nexport interface RecipientStatus {\n id: string;\n name: string;\n status: 'viewed' | 'pending';\n}\n\ninterface AlertsManagerViewProps {\n alertData: AlertViewData;\n isOpen?: boolean;\n onClose?: () => void;\n /** URL da imagem após upload (prioritária - será exibida primeiro) */\n imageLink?: string | null;\n /** Imagem padrão a ser exibida quando não há imagem no alertData (URL string) */\n defaultImage?: string | null;\n // Controle de paginação\n currentPage?: number;\n totalPages?: number;\n onPageChange?: (page: number) => void;\n itemsPerPage?: number;\n}\n\nexport const AlertsManagerView = ({\n alertData,\n isOpen = false,\n onClose,\n imageLink,\n defaultImage,\n currentPage = 1,\n totalPages: externalTotalPages,\n onPageChange,\n itemsPerPage = 10,\n}: AlertsManagerViewProps) => {\n // Calcular paginação (usar props externas se fornecidas, senão calcular)\n const totalPages =\n externalTotalPages ?? Math.ceil(alertData.recipients.length / itemsPerPage);\n\n // Clamp currentPage to valid range\n const effectiveCurrentPage = Math.min(totalPages, Math.max(1, currentPage));\n\n /**\n * Quando o consumidor informa `totalPages` e `onPageChange`, ele já buscou a\n * página no servidor e `recipients` contém apenas os itens dela. Fatiar de novo\n * usaria um índice de página global sobre um array de tamanho de página: na\n * página 2, `slice(10, 20)` sobre 10 itens devolve `[]` e a tabela de\n * Visualizado/Pendente fica vazia da página 2 em diante.\n *\n * Sem essas props o componente continua no modo client-side, recebendo a lista\n * inteira e fatiando — que é como as stories e os testes o usam.\n */\n const isServerPaginated =\n externalTotalPages !== undefined && onPageChange !== undefined;\n\n const startIndex = (effectiveCurrentPage - 1) * itemsPerPage;\n const endIndex = startIndex + itemsPerPage;\n const paginatedRecipients = isServerPaginated\n ? alertData.recipients\n : alertData.recipients.slice(startIndex, endIndex);\n\n const handleClose = () => {\n onClose?.();\n };\n\n const formatDate = (dateInput: string | Date) => {\n const date = dateInput instanceof Date ? dateInput : new Date(dateInput);\n if (Number.isNaN(date.getTime())) return String(dateInput);\n return date.toLocaleDateString('pt-BR', {\n day: '2-digit',\n month: '2-digit',\n year: 'numeric',\n });\n };\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={handleClose}\n title={alertData.title}\n size=\"md\"\n contentClassName=\"p-0\"\n >\n <div className=\"flex flex-col h-[calc(100vh-8rem)] max-h-[700px]\">\n {/* Área de conteúdo com scroll */}\n <div className=\"flex-1 overflow-y-auto px-6 py-4\">\n {/* Preview do alerta */}\n <div className=\"bg-background-50 px-5 py-6 flex flex-col items-center gap-4 rounded-xl mb-4\">\n {/* Prioridade: imageLink > alertData.image > defaultImage */}\n {(imageLink || alertData.image || defaultImage) && (\n <img\n src={imageLink || alertData.image || defaultImage || undefined}\n alt={alertData.title || 'Imagem do alerta'}\n />\n )}\n <div className=\"flex flex-col items-center text-center gap-3\">\n <Text size=\"lg\" weight=\"semibold\">\n {alertData.title || 'Sem Título'}\n </Text>\n <Text size=\"sm\" weight=\"normal\" className=\"text-text-500\">\n {alertData.message || 'Sem mensagem'}\n </Text>\n </div>\n </div>\n\n {/* Divider */}\n <Divider className=\"my-4\" />\n\n {/* Data de envio */}\n <div className=\"flex justify-between items-center mb-4 px-2\">\n <Text size=\"sm\" weight=\"bold\" className=\"text-text-700\">\n Enviado em\n </Text>\n <Text size=\"sm\" weight=\"medium\" className=\"text-text-900\">\n {formatDate(alertData.sentAt)}\n </Text>\n </div>\n\n <Divider className=\"my-4\" />\n\n {/* Tabela de destinatários */}\n <div className=\"mb-4\">\n <Table variant=\"borderless\" className=\"table-fixed\">\n <TableHeader>\n <TableRow variant=\"borderless\">\n <TableHead className=\"py-2 px-3.5 text-start\">\n Destinatário\n </TableHead>\n <TableHead className=\"py-2 px-3.5 w-[120px] text-start\">\n Status\n </TableHead>\n </TableRow>\n </TableHeader>\n <TableBody variant=\"borderless\">\n {paginatedRecipients.map((recipient) => (\n <TableRow key={recipient.id}>\n <TableCell className=\"py-2 px-3.5 flex flex-row gap-2 text-start truncate\">\n <div className=\"rounded-full size-6 bg-primary-100 flex items-center justify-center\">\n <UserIcon className=\"text-primary-950\" size={18} />\n </div>\n {recipient.name}\n </TableCell>\n <TableCell className=\"py-2 px-3.5 text-center\">\n <div className=\"flex justify-center items-center gap-1\">\n {recipient.status === 'viewed' ? (\n <Badge variant=\"solid\" action=\"success\">\n Visualizado\n </Badge>\n ) : (\n <Badge variant=\"solid\" action=\"error\">\n Pendente\n </Badge>\n )}\n </div>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n\n {/* Paginação */}\n {totalPages > 1 && (\n <div className=\"flex justify-end items-center gap-2 bg-background-50 border border-border-200 py-3.5 px-2 rounded-b-2xl\">\n <Text size=\"sm\" className=\"text-text-600\">\n Página {effectiveCurrentPage} de {totalPages}\n </Text>\n <div className=\"flex gap-2\">\n {onPageChange ? (\n <>\n <Button\n variant=\"link\"\n size=\"extra-small\"\n onClick={() =>\n onPageChange(Math.max(1, effectiveCurrentPage - 1))\n }\n disabled={effectiveCurrentPage === 1}\n iconLeft={<CaretLeftIcon />}\n >\n Anterior\n </Button>\n <Button\n variant=\"link\"\n size=\"extra-small\"\n onClick={() =>\n onPageChange(\n Math.min(totalPages, effectiveCurrentPage + 1)\n )\n }\n disabled={effectiveCurrentPage === totalPages}\n iconRight={<CaretRightIcon />}\n >\n Próximo\n </Button>\n </>\n ) : (\n <>\n <Button\n variant=\"link\"\n size=\"extra-small\"\n disabled={effectiveCurrentPage === 1}\n iconLeft={<CaretLeftIcon />}\n >\n Anterior\n </Button>\n <Button\n variant=\"link\"\n size=\"extra-small\"\n disabled={effectiveCurrentPage === totalPages}\n iconRight={<CaretRightIcon />}\n >\n Próximo\n </Button>\n </>\n )}\n </div>\n </div>\n )}\n </div>\n </div>\n </Modal>\n );\n};\n"]}
@@ -6,7 +6,7 @@ var _chunkTN3AYOMVjs = require('./chunk-TN3AYOMV.js');
6
6
  var _react = require('react');
7
7
  var _jsxruntime = require('react/jsx-runtime');
8
8
  var IconButton = _react.forwardRef.call(void 0,
9
- ({ icon, size = "md", active = false, className = "", disabled, ...props }, ref) => {
9
+ ({ icon, size = "md", active, className = "", disabled, ...props }, ref) => {
10
10
  const baseClasses = [
11
11
  "inline-flex",
12
12
  "items-center",
@@ -58,4 +58,4 @@ var IconButton_default = IconButton;
58
58
 
59
59
 
60
60
  exports.IconButton_default = IconButton_default;
61
- //# sourceMappingURL=chunk-BVUPM6EY.js.map
61
+ //# sourceMappingURL=chunk-CAQ2Q3ON.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-CAQ2Q3ON.js","../src/components/IconButton/IconButton.tsx"],"names":[],"mappings":"AAAA;AACE;AACF,sDAA4B;AAC5B;AACA;ACJA,8BAA4D;AAyHpD,+CAAA;AAxDR,IAAM,WAAA,EAAa,+BAAA;AAAA,EACjB,CAAC,EAAE,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,MAAA,EAAQ,UAAA,EAAY,EAAA,EAAI,QAAA,EAAU,GAAG,MAAM,CAAA,EAAG,GAAA,EAAA,GAAQ;AAE1E,IAAA,MAAM,YAAA,EAAc;AAAA,MAClB,aAAA;AAAA,MACA,cAAA;AAAA,MACA,gBAAA;AAAA,MACA,YAAA;AAAA,MACA,aAAA;AAAA,MACA,gBAAA;AAAA,MACA,eAAA;AAAA,MACA,gBAAA;AAAA,MACA,sBAAA;AAAA,MACA,iBAAA;AAAA,MACA,4BAAA;AAAA,MACA,sBAAA;AAAA,MACA,6BAAA;AAAA,MACA,mCAAA;AAAA,MACA,qBAAA;AAAA,MACA,6BAAA;AAAA,MACA;AAAA,IACF,CAAA;AAGA,IAAA,MAAM,YAAA,EAAc;AAAA,MAClB,EAAA,EAAI,CAAC,KAAA,EAAO,KAAA,EAAO,SAAS,CAAA;AAAA,MAC5B,EAAA,EAAI,CAAC,MAAA,EAAQ,MAAA,EAAQ,WAAW;AAAA,IAClC,CAAA;AAGA,IAAA,MAAM,cAAA,EAAgB,OAAA,EAClB,CAAC,gBAAA,EAAkB,mBAAA,EAAqB,uBAAuB,EAAA,EAC/D,CAAC,CAAA;AAEL,IAAA,MAAM,WAAA,EAAa;AAAA,MACjB,GAAG,WAAA;AAAA,MACH,GAAG,WAAA,CAAY,IAAI,CAAA;AAAA,MACnB,GAAG;AAAA,IACL,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAGV,IAAA,MAAM,UAAA,mBAAY,KAAA,CAAM,YAAY,CAAA,UAAK,0BAAA;AAEzC,IAAA,uBACE,6BAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,IAAA,EAAK,QAAA;AAAA,QACL,SAAA,EAAW,iCAAA,UAAG,EAAY,SAAS,CAAA;AAAA,QACnC,QAAA;AAAA,QAIA,cAAA,EAAc,MAAA;AAAA,QACd,YAAA,EAAY,SAAA;AAAA,QACX,GAAG,KAAA;AAAA,QAEJ,QAAA,kBAAA,6BAAA,MAAC,EAAA,EAAK,SAAA,EAAU,kCAAA,EAAoC,QAAA,EAAA,KAAA,CAAK;AAAA,MAAA;AAAA,IAC3D,CAAA;AAAA,EAEJ;AACF,CAAA;AAEA,UAAA,CAAW,YAAA,EAAc,YAAA;AAEzB,IAAO,mBAAA,EAAQ,UAAA;ADzEf;AACA;AACE;AACF,gDAAC","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-CAQ2Q3ON.js","sourcesContent":[null,"import { ButtonHTMLAttributes, ReactNode, forwardRef } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * IconButton component props interface\n */\nexport type IconButtonProps = {\n /** Ícone a ser exibido no botão */\n icon: ReactNode;\n /** Tamanho do botão */\n size?: 'sm' | 'md';\n /** Estado de seleção/ativo do botão - permanece ativo até ser clicado novamente ou outro botão ser ativado */\n active?: boolean;\n /** Additional CSS classes to apply */\n className?: string;\n} & ButtonHTMLAttributes<HTMLButtonElement>;\n\n/**\n * IconButton component for Analytica Ensino platforms\n *\n * Um botão compacto apenas com ícone, ideal para menus dropdown,\n * barras de ferramentas e ações secundárias.\n * Oferece dois tamanhos com estilo consistente.\n * Estado ativo permanece até ser clicado novamente ou outro botão ser ativado.\n * Suporta forwardRef para acesso programático ao elemento DOM.\n *\n * @param icon - O ícone a ser exibido no botão\n * @param size - Tamanho do botão (sm, md)\n * @param active - Estado ativo/selecionado do botão\n * @param className - Classes CSS adicionais\n * @param props - Todos os outros atributos HTML padrão de button\n * @returns Um elemento button compacto estilizado apenas com ícone\n *\n * @example\n * ```tsx\n * <IconButton\n * icon={<MoreVerticalIcon />}\n * size=\"sm\"\n * onClick={() => openMenu()}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Botão ativo em uma barra de ferramentas - permanece ativo até outro clique\n * <IconButton\n * icon={<BoldIcon />}\n * active={isBold}\n * onClick={toggleBold}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Usando ref para controle programático\n * const buttonRef = useRef<HTMLButtonElement>(null);\n *\n * <IconButton\n * ref={buttonRef}\n * icon={<EditIcon />}\n * size=\"md\"\n * onClick={() => startEditing()}\n * />\n * ```\n */\nconst IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(\n ({ icon, size = 'md', active, className = '', disabled, ...props }, ref) => {\n // Classes base para todos os estados\n const baseClasses = [\n 'inline-flex',\n 'items-center',\n 'justify-center',\n 'rounded-lg',\n 'font-medium',\n 'bg-transparent',\n 'text-text-950',\n 'cursor-pointer',\n 'hover:bg-primary-600',\n 'hover:text-text',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-offset-0',\n 'focus-visible:ring-indicator-info',\n 'disabled:opacity-50',\n 'disabled:cursor-not-allowed',\n 'disabled:pointer-events-none',\n ];\n\n // Classes de tamanho\n const sizeClasses = {\n sm: ['w-6', 'h-6', 'text-sm'],\n md: ['w-10', 'h-10', 'text-base'],\n };\n\n // Classes de estado ativo\n const activeClasses = active\n ? ['!bg-primary-50', '!text-primary-950', 'hover:!bg-primary-100']\n : [];\n\n const allClasses = [\n ...baseClasses,\n ...sizeClasses[size],\n ...activeClasses,\n ].join(' ');\n\n // Garantir acessibilidade com aria-label padrão\n const ariaLabel = props['aria-label'] ?? 'Botão de ação';\n\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn(allClasses, className)}\n disabled={disabled}\n // Only expose toggle semantics when the caller actually controls the\n // state; a hardcoded `false` makes screen readers announce every\n // icon button as a toggle (\"botão alternar\").\n aria-pressed={active}\n aria-label={ariaLabel}\n {...props}\n >\n <span className=\"flex items-center justify-center\">{icon}</span>\n </button>\n );\n }\n);\n\nIconButton.displayName = 'IconButton';\n\nexport default IconButton;\n"]}