analytica-frontend-lib 1.7.21 → 1.7.23

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 (384) hide show
  1. package/dist/AccessReportModal/index.js +3 -3
  2. package/dist/AccessReportModal/index.mjs +2 -2
  3. package/dist/ActivitiesHistory/index.js +49 -49
  4. package/dist/ActivitiesHistory/index.mjs +48 -48
  5. package/dist/ActivityCardQuestionBanks/index.js +49 -49
  6. package/dist/ActivityCardQuestionBanks/index.mjs +48 -48
  7. package/dist/ActivityCardQuestionPreview/index.js +49 -49
  8. package/dist/ActivityCardQuestionPreview/index.mjs +48 -48
  9. package/dist/ActivityCreate/ActivityCreateContent/index.js +49 -49
  10. package/dist/ActivityCreate/ActivityCreateContent/index.mjs +48 -48
  11. package/dist/ActivityCreate/index.js +49 -49
  12. package/dist/ActivityCreate/index.mjs +48 -48
  13. package/dist/ActivityDetails/index.js +49 -49
  14. package/dist/ActivityDetails/index.mjs +48 -48
  15. package/dist/ActivityFilters/index.js +49 -49
  16. package/dist/ActivityFilters/index.mjs +48 -48
  17. package/dist/ActivityPageLayout/index.js +49 -49
  18. package/dist/ActivityPageLayout/index.mjs +48 -48
  19. package/dist/ActivityPreview/index.js +49 -49
  20. package/dist/ActivityPreview/index.mjs +48 -48
  21. package/dist/AlertManager/index.js +49 -49
  22. package/dist/AlertManager/index.mjs +48 -48
  23. package/dist/AlertManagerView/index.js +3 -3
  24. package/dist/AlertManagerView/index.mjs +2 -2
  25. package/dist/AnnouncementModal/index.js +3 -3
  26. package/dist/AnnouncementModal/index.mjs +2 -2
  27. package/dist/AnnouncementsGate/index.js +4 -4
  28. package/dist/AnnouncementsGate/index.mjs +3 -3
  29. package/dist/AnswerKeyModal/index.js +3 -3
  30. package/dist/AnswerKeyModal/index.mjs +2 -2
  31. package/dist/AppHeader/index.js +6 -6
  32. package/dist/AppHeader/index.mjs +5 -5
  33. package/dist/CalendarCard/index.js +4 -4
  34. package/dist/CalendarCard/index.mjs +3 -3
  35. package/dist/Chat/index.js +3 -3
  36. package/dist/Chat/index.mjs +2 -2
  37. package/dist/CheckBox/index.d.ts.map +1 -1
  38. package/dist/CheckBox/index.js +2 -2
  39. package/dist/CheckBox/index.mjs +1 -1
  40. package/dist/CheckBoxGroup/index.js +49 -49
  41. package/dist/CheckBoxGroup/index.mjs +48 -48
  42. package/dist/ComparatorChart/ComparatorView/index.js +3 -3
  43. package/dist/ComparatorChart/ComparatorView/index.mjs +2 -2
  44. package/dist/CorrectActivityModal/index.js +7 -7
  45. package/dist/CorrectActivityModal/index.mjs +6 -6
  46. package/dist/DownloadButton/index.js +4 -4
  47. package/dist/DownloadButton/index.mjs +3 -3
  48. package/dist/DownloadModal/index.js +3 -3
  49. package/dist/DownloadModal/index.mjs +2 -2
  50. package/dist/DropdownMenu/index.js +3 -3
  51. package/dist/DropdownMenu/index.mjs +2 -2
  52. package/dist/EnemClassroomStartModal/index.d.ts.map +1 -1
  53. package/dist/EnemClassroomStartModal/index.js +7 -7
  54. package/dist/EnemClassroomStartModal/index.mjs +6 -6
  55. package/dist/EnemMoment/index.js +57 -57
  56. package/dist/EnemMoment/index.mjs +51 -51
  57. package/dist/EnemMomentStudentModal/index.js +50 -50
  58. package/dist/EnemMomentStudentModal/index.mjs +48 -48
  59. package/dist/ExamDetailsLayout/ExamDetailsPage/index.js +49 -49
  60. package/dist/ExamDetailsLayout/ExamDetailsPage/index.mjs +48 -48
  61. package/dist/ExamPageLayout/index.js +49 -49
  62. package/dist/ExamPageLayout/index.mjs +48 -48
  63. package/dist/Forum/index.js +5 -5
  64. package/dist/Forum/index.mjs +4 -4
  65. package/dist/KnowledgePerformanceCard/index.js +51 -51
  66. package/dist/KnowledgePerformanceCard/index.mjs +50 -50
  67. package/dist/LessonCardPreview/index.js +49 -49
  68. package/dist/LessonCardPreview/index.mjs +48 -48
  69. package/dist/LessonMediaSections/index.js +49 -49
  70. package/dist/LessonMediaSections/index.mjs +48 -48
  71. package/dist/LessonsCatalog/index.js +49 -49
  72. package/dist/LessonsCatalog/index.mjs +48 -48
  73. package/dist/Modal/index.d.ts.map +1 -1
  74. package/dist/Modal/index.js +2 -2
  75. package/dist/Modal/index.mjs +1 -1
  76. package/dist/MultiCheckSelect/index.js +5 -5
  77. package/dist/MultiCheckSelect/index.mjs +4 -4
  78. package/dist/MultiSearchSelect/index.js +3 -3
  79. package/dist/MultiSearchSelect/index.mjs +2 -2
  80. package/dist/MultipleChoice/index.js +3 -3
  81. package/dist/MultipleChoice/index.mjs +2 -2
  82. package/dist/NotificationCard/index.js +4 -4
  83. package/dist/NotificationCard/index.mjs +3 -3
  84. package/dist/PerformanceReportModal/index.js +3 -3
  85. package/dist/PerformanceReportModal/index.mjs +2 -2
  86. package/dist/PrintableUsersTable/index.js +49 -49
  87. package/dist/PrintableUsersTable/index.mjs +48 -48
  88. package/dist/ProgressModal/index.js +3 -3
  89. package/dist/ProgressModal/index.mjs +2 -2
  90. package/dist/QuestionsPerformanceCard/index.js +50 -50
  91. package/dist/QuestionsPerformanceCard/index.mjs +49 -49
  92. package/dist/Quiz/index.js +6 -6
  93. package/dist/Quiz/index.mjs +5 -5
  94. package/dist/RecommendedLessonCreate/index.js +49 -49
  95. package/dist/RecommendedLessonCreate/index.mjs +48 -48
  96. package/dist/RecommendedLessonDetails/index.js +7 -7
  97. package/dist/RecommendedLessonDetails/index.mjs +6 -6
  98. package/dist/RecommendedLessonsHistory/index.js +49 -49
  99. package/dist/RecommendedLessonsHistory/index.mjs +48 -48
  100. package/dist/ReportDetailModal/index.js +4 -4
  101. package/dist/ReportDetailModal/index.mjs +3 -3
  102. package/dist/RichEditor/index.js +5 -5
  103. package/dist/RichEditor/index.mjs +4 -4
  104. package/dist/{RichEditorCore-6MTYSSKS.mjs → RichEditorCore-2S5YUAOI.mjs} +5 -5
  105. package/dist/{RichEditorCore-KDARBEK4.js → RichEditorCore-N7A2445B.js} +16 -16
  106. package/dist/{RichEditorCore-KDARBEK4.js.map → RichEditorCore-N7A2445B.js.map} +1 -1
  107. package/dist/Search/index.js +4 -4
  108. package/dist/Search/index.mjs +3 -3
  109. package/dist/SendActivityModal/SendActivityModal.js +49 -49
  110. package/dist/SendActivityModal/SendActivityModal.mjs +48 -48
  111. package/dist/SendActivityModal/index.js +49 -49
  112. package/dist/SendActivityModal/index.mjs +48 -48
  113. package/dist/SendLessonModal/index.js +49 -49
  114. package/dist/SendLessonModal/index.mjs +48 -48
  115. package/dist/SimulatedPerformance/SimulatedPerformanceView/index.js +49 -49
  116. package/dist/SimulatedPerformance/SimulatedPerformanceView/index.mjs +48 -48
  117. package/dist/SimulatedPerformance/useSimulatedPerformance/index.js +5 -5
  118. package/dist/SimulatedPerformance/useSimulatedPerformance/index.mjs +4 -4
  119. package/dist/SimulatedStudentSimulationsModal/index.js +6 -6
  120. package/dist/SimulatedStudentSimulationsModal/index.mjs +5 -5
  121. package/dist/SimulationsPage/index.js +49 -49
  122. package/dist/SimulationsPage/index.mjs +48 -48
  123. package/dist/StudentLessonProgressModal/index.js +3 -3
  124. package/dist/StudentLessonProgressModal/index.mjs +2 -2
  125. package/dist/StudentPerformanceDetailsModal/index.js +5 -5
  126. package/dist/StudentPerformanceDetailsModal/index.mjs +4 -4
  127. package/dist/Support/TicketModal/index.js +3 -3
  128. package/dist/Support/TicketModal/index.mjs +2 -2
  129. package/dist/Support/index.js +4 -4
  130. package/dist/Support/index.mjs +3 -3
  131. package/dist/SupportChatComposer/index.js +3 -3
  132. package/dist/SupportChatComposer/index.mjs +2 -2
  133. package/dist/SupportChatEvent/index.js +3 -3
  134. package/dist/SupportChatEvent/index.mjs +2 -2
  135. package/dist/SupportChatMessage/index.js +3 -3
  136. package/dist/SupportChatMessage/index.mjs +2 -2
  137. package/dist/SupportChatWidget/index.js +3 -3
  138. package/dist/SupportChatWidget/index.mjs +2 -2
  139. package/dist/SupportConcludeModal/index.js +4 -4
  140. package/dist/SupportConcludeModal/index.mjs +3 -3
  141. package/dist/SupportConversationHeader/index.js +4 -4
  142. package/dist/SupportConversationHeader/index.mjs +3 -3
  143. package/dist/SupportConversationMessages/index.js +5 -5
  144. package/dist/SupportConversationMessages/index.mjs +4 -4
  145. package/dist/SupportTicketConversation/index.js +12 -12
  146. package/dist/SupportTicketConversation/index.mjs +11 -11
  147. package/dist/TableProvider/index.js +49 -49
  148. package/dist/TableProvider/index.mjs +48 -48
  149. package/dist/UnifiedDraftModelPage/index.js +49 -49
  150. package/dist/UnifiedDraftModelPage/index.mjs +48 -48
  151. package/dist/UnifiedHistoryPage/index.js +49 -49
  152. package/dist/UnifiedHistoryPage/index.mjs +48 -48
  153. package/dist/VideoPlayer/index.js +5 -5
  154. package/dist/VideoPlayer/index.mjs +4 -4
  155. package/dist/Whiteboard/index.js +3 -3
  156. package/dist/Whiteboard/index.mjs +2 -2
  157. package/dist/{chunk-B6QOBHZM.mjs → chunk-24BEZK43.mjs} +2 -2
  158. package/dist/{chunk-CYSODZGX.js → chunk-27Y6V3GM.js} +169 -66
  159. package/dist/chunk-27Y6V3GM.js.map +1 -0
  160. package/dist/{chunk-OQTXTDSK.mjs → chunk-2OJ3QSLU.mjs} +3 -3
  161. package/dist/{chunk-VDUO65VK.mjs → chunk-3EGJJDIM.mjs} +2 -2
  162. package/dist/{chunk-VDUO65VK.mjs.map → chunk-3EGJJDIM.mjs.map} +1 -1
  163. package/dist/{chunk-4OUBPEVS.mjs → chunk-3HKLGNUE.mjs} +3 -3
  164. package/dist/{chunk-YA7XLH7D.js → chunk-3MFACEZ2.js} +3 -3
  165. package/dist/{chunk-YA7XLH7D.js.map → chunk-3MFACEZ2.js.map} +1 -1
  166. package/dist/{chunk-IHBWJM4C.mjs → chunk-3Q7RJKUP.mjs} +2 -2
  167. package/dist/{chunk-PY6WD6SB.mjs → chunk-3WRB4WCD.mjs} +4 -4
  168. package/dist/{chunk-4RHCBMMP.js → chunk-46VHAL4S.js} +3 -3
  169. package/dist/{chunk-4RHCBMMP.js.map → chunk-46VHAL4S.js.map} +1 -1
  170. package/dist/{chunk-POT3ALY3.js → chunk-5DINXU6W.js} +1 -1
  171. package/dist/{chunk-POT3ALY3.js.map → chunk-5DINXU6W.js.map} +1 -1
  172. package/dist/{chunk-L52NFNFK.mjs → chunk-5VC4BK65.mjs} +3 -3
  173. package/dist/{chunk-B67RLIUM.js → chunk-72D7G6YQ.js} +3 -3
  174. package/dist/{chunk-B67RLIUM.js.map → chunk-72D7G6YQ.js.map} +1 -1
  175. package/dist/{chunk-C65DG2RI.mjs → chunk-7FSBWUOM.mjs} +2 -2
  176. package/dist/{chunk-QWMFWYII.js → chunk-7IZNLARX.js} +3 -3
  177. package/dist/{chunk-QWMFWYII.js.map → chunk-7IZNLARX.js.map} +1 -1
  178. package/dist/{chunk-ZCBT2BUZ.js → chunk-7RFHXFFV.js} +13 -13
  179. package/dist/{chunk-ZCBT2BUZ.js.map → chunk-7RFHXFFV.js.map} +1 -1
  180. package/dist/{chunk-EMWX6IKP.js → chunk-7VJP5D26.js} +13 -13
  181. package/dist/{chunk-EMWX6IKP.js.map → chunk-7VJP5D26.js.map} +1 -1
  182. package/dist/{chunk-HCQJR5CE.mjs → chunk-7ZWEBNFB.mjs} +2 -2
  183. package/dist/{chunk-3RHNJUXU.mjs → chunk-ACSEJOW6.mjs} +2 -2
  184. package/dist/{chunk-WQZHUW7C.mjs → chunk-ANFHLSDB.mjs} +3 -3
  185. package/dist/{chunk-H6NQOLQY.mjs → chunk-ANFLWCVD.mjs} +2 -2
  186. package/dist/{chunk-BKBDQXZX.js → chunk-AOYPUZRO.js} +3 -3
  187. package/dist/{chunk-BKBDQXZX.js.map → chunk-AOYPUZRO.js.map} +1 -1
  188. package/dist/{chunk-5WS6IENQ.mjs → chunk-AU2HLQDJ.mjs} +2 -2
  189. package/dist/{chunk-P6FX3NT6.mjs → chunk-AZGDHAP5.mjs} +2 -2
  190. package/dist/{chunk-NCGO7FPB.js → chunk-AZKNYREH.js} +8 -8
  191. package/dist/{chunk-NCGO7FPB.js.map → chunk-AZKNYREH.js.map} +1 -1
  192. package/dist/{chunk-R6MTPYYW.js → chunk-AZTBNERQ.js} +3 -3
  193. package/dist/{chunk-R6MTPYYW.js.map → chunk-AZTBNERQ.js.map} +1 -1
  194. package/dist/{chunk-NPB2JTYC.mjs → chunk-BO3QVQ37.mjs} +4 -4
  195. package/dist/{chunk-2WI4Q2I2.mjs → chunk-BPMWRFSI.mjs} +4 -4
  196. package/dist/{chunk-TEW33GLR.mjs → chunk-BXN3VO6I.mjs} +2 -2
  197. package/dist/{chunk-7ZB3YHF2.mjs → chunk-C6LC5LD5.mjs} +1 -1
  198. package/dist/{chunk-7ZB3YHF2.mjs.map → chunk-C6LC5LD5.mjs.map} +1 -1
  199. package/dist/{chunk-5HONWYQA.js → chunk-C7ILTLT5.js} +3 -3
  200. package/dist/{chunk-5HONWYQA.js.map → chunk-C7ILTLT5.js.map} +1 -1
  201. package/dist/{chunk-OES54VU6.mjs → chunk-CFV2OZ3P.mjs} +116 -13
  202. package/dist/chunk-CFV2OZ3P.mjs.map +1 -0
  203. package/dist/{chunk-4OVIHZ3I.js → chunk-CGXWWPEA.js} +6 -6
  204. package/dist/{chunk-4OVIHZ3I.js.map → chunk-CGXWWPEA.js.map} +1 -1
  205. package/dist/{chunk-MNU45FM3.js → chunk-COT4KW3H.js} +2 -2
  206. package/dist/{chunk-MNU45FM3.js.map → chunk-COT4KW3H.js.map} +1 -1
  207. package/dist/{chunk-JDQUXG75.js → chunk-CVDNCU6L.js} +3 -3
  208. package/dist/{chunk-JDQUXG75.js.map → chunk-CVDNCU6L.js.map} +1 -1
  209. package/dist/{chunk-HBZHWSMZ.mjs → chunk-D46PJSQ7.mjs} +13 -13
  210. package/dist/{chunk-ARYIJ4RU.js → chunk-DDORKDQO.js} +3 -3
  211. package/dist/{chunk-ARYIJ4RU.js.map → chunk-DDORKDQO.js.map} +1 -1
  212. package/dist/{chunk-7YQTDJFA.js → chunk-DKXSR2V3.js} +3 -3
  213. package/dist/{chunk-7YQTDJFA.js.map → chunk-DKXSR2V3.js.map} +1 -1
  214. package/dist/{chunk-I6EAPPBL.mjs → chunk-DZMYIOKZ.mjs} +5 -5
  215. package/dist/{chunk-SKM7NFPY.js → chunk-EPXAFJVW.js} +6 -6
  216. package/dist/{chunk-SKM7NFPY.js.map → chunk-EPXAFJVW.js.map} +1 -1
  217. package/dist/{chunk-AYYMS23L.js → chunk-ESIQ2GB4.js} +7 -7
  218. package/dist/{chunk-AYYMS23L.js.map → chunk-ESIQ2GB4.js.map} +1 -1
  219. package/dist/{chunk-NU2WNYOD.js → chunk-EW7HGUFV.js} +87 -87
  220. package/dist/{chunk-NU2WNYOD.js.map → chunk-EW7HGUFV.js.map} +1 -1
  221. package/dist/{chunk-I4QGQMXI.mjs → chunk-EXYXZFNK.mjs} +2 -2
  222. package/dist/{chunk-RBJAO57A.js → chunk-FAAR4XC5.js} +3 -3
  223. package/dist/{chunk-RBJAO57A.js.map → chunk-FAAR4XC5.js.map} +1 -1
  224. package/dist/{chunk-N2VNE3ON.js → chunk-FDTDJDZI.js} +13 -13
  225. package/dist/{chunk-N2VNE3ON.js.map → chunk-FDTDJDZI.js.map} +1 -1
  226. package/dist/{chunk-SU5Q64TW.mjs → chunk-FVCTKS33.mjs} +6 -5
  227. package/dist/chunk-FVCTKS33.mjs.map +1 -0
  228. package/dist/{chunk-AVHZHWI3.js → chunk-GBIFSFWJ.js} +15 -15
  229. package/dist/{chunk-AVHZHWI3.js.map → chunk-GBIFSFWJ.js.map} +1 -1
  230. package/dist/{chunk-KSUPPAKH.js → chunk-GIQSHTB5.js} +9 -9
  231. package/dist/{chunk-KSUPPAKH.js.map → chunk-GIQSHTB5.js.map} +1 -1
  232. package/dist/{chunk-4QBKD4JN.js → chunk-GUUJYGXF.js} +3 -3
  233. package/dist/{chunk-4QBKD4JN.js.map → chunk-GUUJYGXF.js.map} +1 -1
  234. package/dist/{chunk-M4O46VDY.mjs → chunk-GX2PKLN2.mjs} +2 -2
  235. package/dist/{chunk-5BUTINIM.mjs → chunk-HLVLSWKD.mjs} +3 -3
  236. package/dist/{chunk-FU23WOTY.js → chunk-HSV5VZ4Z.js} +6 -6
  237. package/dist/{chunk-FU23WOTY.js.map → chunk-HSV5VZ4Z.js.map} +1 -1
  238. package/dist/{chunk-MMS2ELDK.mjs → chunk-IIQCS3A2.mjs} +2 -2
  239. package/dist/{chunk-NKWLCZUZ.mjs → chunk-JGGUMHM6.mjs} +2 -2
  240. package/dist/{chunk-HT2GREFH.js → chunk-JLD46DL7.js} +11 -11
  241. package/dist/{chunk-HT2GREFH.js.map → chunk-JLD46DL7.js.map} +1 -1
  242. package/dist/{chunk-NVL5TKWD.mjs → chunk-JYFLOQYT.mjs} +2 -2
  243. package/dist/{chunk-DWGDEUEW.mjs → chunk-KG563P7F.mjs} +5 -5
  244. package/dist/{chunk-TCIC5556.mjs → chunk-KH4TEDGD.mjs} +2 -2
  245. package/dist/{chunk-23PAYKWB.js → chunk-KM27ZDHZ.js} +2 -2
  246. package/dist/{chunk-23PAYKWB.js.map → chunk-KM27ZDHZ.js.map} +1 -1
  247. package/dist/{chunk-XZUMD3CB.mjs → chunk-LODDYDTP.mjs} +2 -2
  248. package/dist/{chunk-XZUMD3CB.mjs.map → chunk-LODDYDTP.mjs.map} +1 -1
  249. package/dist/{chunk-E6CGJBID.mjs → chunk-LUHKMY56.mjs} +2 -2
  250. package/dist/{chunk-TVLEUNKE.mjs → chunk-LZP5VZCO.mjs} +2 -2
  251. package/dist/{chunk-EHNEBXS7.mjs → chunk-M6DXZ2FM.mjs} +2 -2
  252. package/dist/{chunk-B4C5DWAP.mjs → chunk-MMMJDO75.mjs} +2 -2
  253. package/dist/{chunk-AFCWMJQO.js → chunk-MOUPNP53.js} +3 -3
  254. package/dist/{chunk-AFCWMJQO.js.map → chunk-MOUPNP53.js.map} +1 -1
  255. package/dist/{chunk-36KFIZ43.js → chunk-MYPUZ6EG.js} +3 -3
  256. package/dist/{chunk-36KFIZ43.js.map → chunk-MYPUZ6EG.js.map} +1 -1
  257. package/dist/{chunk-6JHIKIPA.js → chunk-NUGLTWEA.js} +23 -23
  258. package/dist/{chunk-6JHIKIPA.js.map → chunk-NUGLTWEA.js.map} +1 -1
  259. package/dist/{chunk-KWXOYHYK.mjs → chunk-P7MORXSE.mjs} +2 -2
  260. package/dist/{chunk-YG4IZ5Q3.mjs → chunk-Q2OSEROT.mjs} +2 -2
  261. package/dist/{chunk-L5SXG6T6.mjs → chunk-QQKOHKO2.mjs} +3 -3
  262. package/dist/{chunk-SADSJ2MV.js → chunk-QVGUYPXO.js} +3 -3
  263. package/dist/{chunk-SADSJ2MV.js.map → chunk-QVGUYPXO.js.map} +1 -1
  264. package/dist/{chunk-K45PTE7F.mjs → chunk-RBKQXT73.mjs} +4 -4
  265. package/dist/{chunk-JZFISWTP.js → chunk-RIN2IN7E.js} +2 -2
  266. package/dist/{chunk-JZFISWTP.js.map → chunk-RIN2IN7E.js.map} +1 -1
  267. package/dist/{chunk-BXLAOYAG.mjs → chunk-RIUVPTG7.mjs} +2 -2
  268. package/dist/{chunk-QVB54LBS.mjs → chunk-RNZGX6MX.mjs} +3 -3
  269. package/dist/{chunk-VSVLIRO2.js → chunk-RXEU77VN.js} +15 -15
  270. package/dist/{chunk-VSVLIRO2.js.map → chunk-RXEU77VN.js.map} +1 -1
  271. package/dist/{chunk-ZRIYCU2V.js → chunk-S2BZPPAI.js} +5 -5
  272. package/dist/{chunk-ZRIYCU2V.js.map → chunk-S2BZPPAI.js.map} +1 -1
  273. package/dist/{chunk-MES2BP4T.mjs → chunk-SGL5IUCV.mjs} +2 -2
  274. package/dist/{chunk-7LS5UQCQ.mjs → chunk-STKUPDHG.mjs} +2 -2
  275. package/dist/{chunk-PPYCCT2K.js → chunk-SWDVSCQJ.js} +9 -8
  276. package/dist/chunk-SWDVSCQJ.js.map +1 -0
  277. package/dist/{chunk-JCA76TCX.mjs → chunk-SZKLJOTN.mjs} +2 -2
  278. package/dist/{chunk-F5WR6XZZ.js → chunk-T73HPYF2.js} +3 -3
  279. package/dist/{chunk-F5WR6XZZ.js.map → chunk-T73HPYF2.js.map} +1 -1
  280. package/dist/{chunk-ZB6HDDE4.js → chunk-TQO37PVW.js} +14 -14
  281. package/dist/{chunk-ZB6HDDE4.js.map → chunk-TQO37PVW.js.map} +1 -1
  282. package/dist/{chunk-VKQ5AJDT.mjs → chunk-UENZQF2Y.mjs} +2 -2
  283. package/dist/{chunk-DCG7EUBC.js → chunk-UWLW5YVB.js} +6 -6
  284. package/dist/{chunk-DCG7EUBC.js.map → chunk-UWLW5YVB.js.map} +1 -1
  285. package/dist/{chunk-37AJ2GVO.mjs → chunk-UXJIZDXQ.mjs} +2 -2
  286. package/dist/{chunk-PLLPFCN3.js → chunk-UXMDPRAI.js} +14 -14
  287. package/dist/{chunk-PLLPFCN3.js.map → chunk-UXMDPRAI.js.map} +1 -1
  288. package/dist/{chunk-7BOTKRY6.mjs → chunk-V55DG5B4.mjs} +2 -2
  289. package/dist/{chunk-YRTIDZTN.js → chunk-VHEDMUAW.js} +4 -4
  290. package/dist/{chunk-YRTIDZTN.js.map → chunk-VHEDMUAW.js.map} +1 -1
  291. package/dist/{chunk-4RD55E4R.js → chunk-VWLWSO4J.js} +3 -3
  292. package/dist/{chunk-4RD55E4R.js.map → chunk-VWLWSO4J.js.map} +1 -1
  293. package/dist/{chunk-VJQIKFMG.mjs → chunk-W3CWEZDB.mjs} +3 -3
  294. package/dist/{chunk-JM6SLU7D.js → chunk-W7NNH4VY.js} +6 -6
  295. package/dist/{chunk-JM6SLU7D.js.map → chunk-W7NNH4VY.js.map} +1 -1
  296. package/dist/{chunk-VQ7MSYIH.js → chunk-WUN5NHZE.js} +4 -4
  297. package/dist/{chunk-VQ7MSYIH.js.map → chunk-WUN5NHZE.js.map} +1 -1
  298. package/dist/{chunk-AAU7ABJN.js → chunk-WWTRSQ3X.js} +3 -3
  299. package/dist/{chunk-AAU7ABJN.js.map → chunk-WWTRSQ3X.js.map} +1 -1
  300. package/dist/{chunk-BR6D2TGF.js → chunk-WZOA2PVJ.js} +3 -3
  301. package/dist/{chunk-BR6D2TGF.js.map → chunk-WZOA2PVJ.js.map} +1 -1
  302. package/dist/{chunk-TZKR5Z4J.mjs → chunk-XJF2F36N.mjs} +2 -2
  303. package/dist/{chunk-P6ACVNQK.mjs → chunk-YD2XBEKO.mjs} +2 -2
  304. package/dist/{chunk-LMXWIPGR.js → chunk-YWPLHFAK.js} +3 -3
  305. package/dist/{chunk-LMXWIPGR.js.map → chunk-YWPLHFAK.js.map} +1 -1
  306. package/dist/{chunk-DCEWWHV5.mjs → chunk-Z3HGYPH7.mjs} +2 -2
  307. package/dist/{chunk-Q3PKW6TH.js → chunk-ZCV3HWI2.js} +6 -6
  308. package/dist/{chunk-Q3PKW6TH.js.map → chunk-ZCV3HWI2.js.map} +1 -1
  309. package/dist/{chunk-OSA3YVDZ.mjs → chunk-ZF46DCAC.mjs} +2 -2
  310. package/dist/{chunk-5T6IDXSB.js → chunk-ZO2GA23I.js} +3 -3
  311. package/dist/{chunk-5T6IDXSB.js.map → chunk-ZO2GA23I.js.map} +1 -1
  312. package/dist/{chunk-ZN3763VZ.js → chunk-ZQLW4EJB.js} +9 -9
  313. package/dist/{chunk-ZN3763VZ.js.map → chunk-ZQLW4EJB.js.map} +1 -1
  314. package/dist/{chunk-CM5R4SPF.js → chunk-ZQQJTO6T.js} +3 -3
  315. package/dist/{chunk-CM5R4SPF.js.map → chunk-ZQQJTO6T.js.map} +1 -1
  316. package/dist/{chunk-SEL4QDCO.js → chunk-ZSXSAE3D.js} +3 -3
  317. package/dist/{chunk-SEL4QDCO.js.map → chunk-ZSXSAE3D.js.map} +1 -1
  318. package/dist/components/SupportChatWidget/guestSupportChatAdapter.d.ts +6 -1
  319. package/dist/components/SupportChatWidget/guestSupportChatAdapter.d.ts.map +1 -1
  320. package/dist/hooks/useAppContent/index.js +49 -49
  321. package/dist/hooks/useAppContent/index.mjs +48 -48
  322. package/dist/hooks/useSupportChat.d.ts +2 -0
  323. package/dist/hooks/useSupportChat.d.ts.map +1 -1
  324. package/dist/index.js +49 -49
  325. package/dist/index.mjs +48 -48
  326. package/dist/types/supportChat/index.d.ts +14 -0
  327. package/dist/types/supportChat/index.d.ts.map +1 -1
  328. package/dist/types/supportChat/index.js +2 -2
  329. package/dist/types/supportChat/index.mjs +1 -1
  330. package/dist/types/supportChat.d.ts +14 -0
  331. package/dist/types/supportChat.d.ts.map +1 -1
  332. package/package.json +1 -1
  333. package/dist/chunk-CYSODZGX.js.map +0 -1
  334. package/dist/chunk-OES54VU6.mjs.map +0 -1
  335. package/dist/chunk-PPYCCT2K.js.map +0 -1
  336. package/dist/chunk-SU5Q64TW.mjs.map +0 -1
  337. /package/dist/{RichEditorCore-6MTYSSKS.mjs.map → RichEditorCore-2S5YUAOI.mjs.map} +0 -0
  338. /package/dist/{chunk-B6QOBHZM.mjs.map → chunk-24BEZK43.mjs.map} +0 -0
  339. /package/dist/{chunk-OQTXTDSK.mjs.map → chunk-2OJ3QSLU.mjs.map} +0 -0
  340. /package/dist/{chunk-4OUBPEVS.mjs.map → chunk-3HKLGNUE.mjs.map} +0 -0
  341. /package/dist/{chunk-IHBWJM4C.mjs.map → chunk-3Q7RJKUP.mjs.map} +0 -0
  342. /package/dist/{chunk-PY6WD6SB.mjs.map → chunk-3WRB4WCD.mjs.map} +0 -0
  343. /package/dist/{chunk-L52NFNFK.mjs.map → chunk-5VC4BK65.mjs.map} +0 -0
  344. /package/dist/{chunk-C65DG2RI.mjs.map → chunk-7FSBWUOM.mjs.map} +0 -0
  345. /package/dist/{chunk-HCQJR5CE.mjs.map → chunk-7ZWEBNFB.mjs.map} +0 -0
  346. /package/dist/{chunk-3RHNJUXU.mjs.map → chunk-ACSEJOW6.mjs.map} +0 -0
  347. /package/dist/{chunk-WQZHUW7C.mjs.map → chunk-ANFHLSDB.mjs.map} +0 -0
  348. /package/dist/{chunk-H6NQOLQY.mjs.map → chunk-ANFLWCVD.mjs.map} +0 -0
  349. /package/dist/{chunk-5WS6IENQ.mjs.map → chunk-AU2HLQDJ.mjs.map} +0 -0
  350. /package/dist/{chunk-P6FX3NT6.mjs.map → chunk-AZGDHAP5.mjs.map} +0 -0
  351. /package/dist/{chunk-NPB2JTYC.mjs.map → chunk-BO3QVQ37.mjs.map} +0 -0
  352. /package/dist/{chunk-2WI4Q2I2.mjs.map → chunk-BPMWRFSI.mjs.map} +0 -0
  353. /package/dist/{chunk-TEW33GLR.mjs.map → chunk-BXN3VO6I.mjs.map} +0 -0
  354. /package/dist/{chunk-HBZHWSMZ.mjs.map → chunk-D46PJSQ7.mjs.map} +0 -0
  355. /package/dist/{chunk-I6EAPPBL.mjs.map → chunk-DZMYIOKZ.mjs.map} +0 -0
  356. /package/dist/{chunk-I4QGQMXI.mjs.map → chunk-EXYXZFNK.mjs.map} +0 -0
  357. /package/dist/{chunk-M4O46VDY.mjs.map → chunk-GX2PKLN2.mjs.map} +0 -0
  358. /package/dist/{chunk-5BUTINIM.mjs.map → chunk-HLVLSWKD.mjs.map} +0 -0
  359. /package/dist/{chunk-MMS2ELDK.mjs.map → chunk-IIQCS3A2.mjs.map} +0 -0
  360. /package/dist/{chunk-NKWLCZUZ.mjs.map → chunk-JGGUMHM6.mjs.map} +0 -0
  361. /package/dist/{chunk-NVL5TKWD.mjs.map → chunk-JYFLOQYT.mjs.map} +0 -0
  362. /package/dist/{chunk-DWGDEUEW.mjs.map → chunk-KG563P7F.mjs.map} +0 -0
  363. /package/dist/{chunk-TCIC5556.mjs.map → chunk-KH4TEDGD.mjs.map} +0 -0
  364. /package/dist/{chunk-E6CGJBID.mjs.map → chunk-LUHKMY56.mjs.map} +0 -0
  365. /package/dist/{chunk-TVLEUNKE.mjs.map → chunk-LZP5VZCO.mjs.map} +0 -0
  366. /package/dist/{chunk-EHNEBXS7.mjs.map → chunk-M6DXZ2FM.mjs.map} +0 -0
  367. /package/dist/{chunk-B4C5DWAP.mjs.map → chunk-MMMJDO75.mjs.map} +0 -0
  368. /package/dist/{chunk-KWXOYHYK.mjs.map → chunk-P7MORXSE.mjs.map} +0 -0
  369. /package/dist/{chunk-YG4IZ5Q3.mjs.map → chunk-Q2OSEROT.mjs.map} +0 -0
  370. /package/dist/{chunk-L5SXG6T6.mjs.map → chunk-QQKOHKO2.mjs.map} +0 -0
  371. /package/dist/{chunk-K45PTE7F.mjs.map → chunk-RBKQXT73.mjs.map} +0 -0
  372. /package/dist/{chunk-BXLAOYAG.mjs.map → chunk-RIUVPTG7.mjs.map} +0 -0
  373. /package/dist/{chunk-QVB54LBS.mjs.map → chunk-RNZGX6MX.mjs.map} +0 -0
  374. /package/dist/{chunk-MES2BP4T.mjs.map → chunk-SGL5IUCV.mjs.map} +0 -0
  375. /package/dist/{chunk-7LS5UQCQ.mjs.map → chunk-STKUPDHG.mjs.map} +0 -0
  376. /package/dist/{chunk-JCA76TCX.mjs.map → chunk-SZKLJOTN.mjs.map} +0 -0
  377. /package/dist/{chunk-VKQ5AJDT.mjs.map → chunk-UENZQF2Y.mjs.map} +0 -0
  378. /package/dist/{chunk-37AJ2GVO.mjs.map → chunk-UXJIZDXQ.mjs.map} +0 -0
  379. /package/dist/{chunk-7BOTKRY6.mjs.map → chunk-V55DG5B4.mjs.map} +0 -0
  380. /package/dist/{chunk-VJQIKFMG.mjs.map → chunk-W3CWEZDB.mjs.map} +0 -0
  381. /package/dist/{chunk-TZKR5Z4J.mjs.map → chunk-XJF2F36N.mjs.map} +0 -0
  382. /package/dist/{chunk-P6ACVNQK.mjs.map → chunk-YD2XBEKO.mjs.map} +0 -0
  383. /package/dist/{chunk-DCEWWHV5.mjs.map → chunk-Z3HGYPH7.mjs.map} +0 -0
  384. /package/dist/{chunk-OSA3YVDZ.mjs.map → chunk-ZF46DCAC.mjs.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-CYSODZGX.js","../src/components/SupportChatWidget/SupportChatWidget.tsx","../src/hooks/useSupportChat.ts","../src/components/SupportChatWidget/SupportChatContactForm.tsx","../src/components/SupportChatWidget/SupportChatArticleView.tsx","../src/components/SupportChatWidget/supportChatContent.tsx","../src/components/SupportChatWidget/NativeSupportChat.tsx","../src/components/SupportChatWidget/guestSupportChatAdapter.ts","../src/components/SupportChatWidget/mockSupportChatAdapter.ts"],"names":["useState","jsxs","jsx","useCallback","useMemo"],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-CYSODZGX.js","sourcesContent":[null,"import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type ChangeEvent,\n type KeyboardEvent,\n} from 'react';\nimport {\n ChatCircleDotsIcon,\n MinusIcon,\n PaperclipIcon,\n PaperPlaneTiltIcon,\n XIcon,\n} from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport { useSupportChat } from '../../hooks/useSupportChat';\nimport { SupportChatContactForm } from './SupportChatContactForm';\nimport { SupportChatArticleView } from './SupportChatArticleView';\nimport {\n AttachmentList,\n MessageText,\n PendingAttachment,\n SUPPORT_CHAT_ACCEPT,\n pickAttachments,\n} from './supportChatContent';\nimport { SupportType } from '../../types/support';\nimport {\n SUPPORT_CHAT_AUTHOR,\n SUPPORT_CHAT_MAX_ATTACHMENTS,\n type SupportChatAdapter,\n type SupportChatFlags,\n type SupportChatMessage,\n type SupportChatTheme,\n type SupportChatUser,\n} from '../../types/supportChat';\nimport { cn } from '../../utils/utils';\n\n/** Visibility of the chat window */\nexport type SupportChatWindowState = 'closed' | 'open' | 'minimized';\n\nexport interface SupportChatWidgetProps {\n /** Data layer (createMockSupportChatAdapter for now) */\n adapter: SupportChatAdapter;\n /** App the widget runs in, sent with the ticket (e.g. \"login\", \"backoffice\") */\n channel: string;\n institutionId?: string;\n /** Logged user; omit for visitors */\n user?: SupportChatUser;\n flags?: SupportChatFlags;\n theme?: SupportChatTheme;\n onOpen?: () => void;\n onSend?: (message: SupportChatMessage) => void;\n /**\n * Called when the window is closed with X. Closing only hides it: the\n * conversation stays connected and new replies count as unread.\n */\n onClose?: () => void;\n /**\n * Link of a FAQ article, e.g. `(id) => '/artigo/' + id`. With it, the\n * \"Baseado na FAQ\" line under an AI answer opens the article; without it\n * the line only names the article.\n */\n faqArticleUrl?: (articleId: string) => string;\n}\n\n/** Label read by screen readers on the unread badge of the launcher */\nconst formatNewMessagesLabel = (count: number): string =>\n count === 1 ? '1 nova mensagem' : `${count} novas mensagens`;\n\n/** The requester side of the conversation (user or visitor) */\nconst isOwnMessage = (message: SupportChatMessage) =>\n message.senderRole === SUPPORT_CHAT_AUTHOR.USER ||\n message.senderRole === SUPPORT_CHAT_AUTHOR.GUEST;\n\nconst formatTime = (createdAt: string) =>\n new Date(createdAt).toLocaleTimeString('pt-BR', {\n hour: '2-digit',\n minute: '2-digit',\n });\n\n/** \"Baseado na FAQ: <title>\" under an AI answer, linked when possible */\nconst FaqReference = ({\n faq,\n faqArticleUrl,\n onOpenArticle,\n}: {\n faq: { id: string; title: string };\n faqArticleUrl?: (articleId: string) => string;\n onOpenArticle?: (article: { id: string; title: string }) => void;\n}) => {\n const linked = !!onOpenArticle || !!faqArticleUrl;\n const label = (\n <Text\n as=\"span\"\n size=\"xs\"\n color=\"text-primary-700\"\n className={cn(linked && 'underline')}\n >\n Baseado na FAQ: {faq.title}\n </Text>\n );\n let content = label;\n if (onOpenArticle) {\n // Opens inside the chat, like the Zendesk messenger (no new tab)\n content = (\n <a\n href={faqArticleUrl?.(faq.id) ?? '#'}\n onClick={(event) => {\n event.preventDefault();\n onOpenArticle(faq);\n }}\n >\n {label}\n </a>\n );\n } else if (faqArticleUrl) {\n content = (\n <a href={faqArticleUrl(faq.id)} target=\"_blank\" rel=\"noreferrer\">\n {label}\n </a>\n );\n }\n return (\n <div data-testid=\"support-chat-faq\" className=\"mt-1 max-w-[80%]\">\n {content}\n </div>\n );\n};\n\nconst MessageBubble = ({\n message,\n faqArticleUrl,\n onOpenArticle,\n}: {\n message: SupportChatMessage;\n faqArticleUrl?: (articleId: string) => string;\n onOpenArticle?: (article: { id: string; title?: string }) => void;\n}) => {\n const own = isOwnMessage(message);\n return (\n <div\n className={cn(\n 'flex flex-col mb-3 transition-opacity',\n own ? 'items-end' : 'items-start',\n message.pending && 'opacity-60'\n )}\n aria-busy={message.pending || undefined}\n >\n {!own && (\n <Text size=\"xs\" className=\"text-text-500 mb-1\">\n {message.senderName}\n </Text>\n )}\n <div\n className={cn(\n 'max-w-[80%] rounded-2xl whitespace-pre-wrap break-words',\n // Image-only messages show the image without the colored bubble\n message.content\n ? cn(\n 'px-4 py-2',\n own\n ? 'bg-primary-500 text-white rounded-br-md'\n : 'bg-background-100 text-text-900 rounded-bl-md'\n )\n : 'text-text-900'\n )}\n >\n {message.content && (\n <MessageText\n text={message.content}\n onOpenArticle={\n onOpenArticle ? (id) => onOpenArticle({ id }) : undefined\n }\n />\n )}\n {message.attachments.length > 0 && (\n <AttachmentList items={message.attachments} />\n )}\n </div>\n {message.faq && (\n <FaqReference\n faq={message.faq}\n faqArticleUrl={faqArticleUrl}\n onOpenArticle={onOpenArticle}\n />\n )}\n <Text size=\"xs\" className=\"text-text-400 mt-1\">\n {message.pending ? 'Enviando…' : formatTime(message.createdAt)}\n </Text>\n </div>\n );\n};\n\n/**\n * Floating support chat (native support, the Zendesk replacement).\n * Renders nothing when disabled or when the institution uses Zendesk.\n */\nexport const SupportChatWidget = ({\n flags,\n ...props\n}: SupportChatWidgetProps) => {\n const hidden =\n flags?.enabled === false ||\n flags?.chat === false ||\n flags?.supportType === SupportType.ZENDESK;\n\n if (hidden) return null;\n return <SupportChatWidgetContent {...props} />;\n};\n\nconst SupportChatWidgetContent = ({\n adapter,\n channel,\n institutionId,\n user,\n theme,\n onOpen,\n onSend,\n onClose,\n faqArticleUrl,\n}: Omit<SupportChatWidgetProps, 'flags'>) => {\n const [windowState, setWindowState] =\n useState<SupportChatWindowState>('closed');\n const [draft, setDraft] = useState('');\n const [files, setFiles] = useState<File[]>([]);\n const [fileError, setFileError] = useState<string | null>(null);\n const [unread, setUnread] = useState(0);\n // FAQ article open inside the chat (the conversation stays as it was)\n const [article, setArticle] = useState<{ id: string; title?: string } | null>(\n null\n );\n const [expanded, setExpanded] = useState(false);\n const listRef = useRef<HTMLDivElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const seenCount = useRef(0);\n\n const { messages, sending, error, closed, send, updateContact, reset } =\n useSupportChat({\n adapter,\n channel,\n institutionId,\n user,\n });\n const [savedContactRequests, setSavedContactRequests] = useState<string[]>(\n []\n );\n\n // Latest AI request for name/e-mail: the form goes under it, and the\n // composer waits until it is sent\n const contactRequest = updateContact\n ? [...messages].reverse().find((message) => message.contactRequest?.length)\n : undefined;\n const awaitingContact =\n !!contactRequest && !savedContactRequests.includes(contactRequest.id);\n\n const isOpen = windowState === 'open';\n\n const canOpenArticle = !!adapter.getArticle && !!institutionId;\n const loadArticle = useCallback(\n (articleId: string) => {\n if (!adapter.getArticle || !institutionId) {\n return Promise.reject(new Error('Artigo indisponível'));\n }\n // Called on the adapter so a class-based adapter keeps its `this`\n return adapter.getArticle(institutionId, articleId);\n },\n [adapter, institutionId]\n );\n const openArticle = canOpenArticle\n ? (target: { id: string; title?: string }) => {\n setArticle(target);\n setExpanded(true);\n }\n : undefined;\n const closeArticle = () => {\n setArticle(null);\n setExpanded(false);\n };\n const title = theme?.title ?? 'Suporte';\n const left = theme?.position === 'bottom-left';\n const accentStyle = theme?.primaryColor\n ? { backgroundColor: theme.primaryColor }\n : undefined;\n\n // Count the support replies that arrive while the window is not visible\n useEffect(() => {\n const fresh = messages.slice(seenCount.current);\n seenCount.current = messages.length;\n if (isOpen) return;\n const replies = fresh.filter((message) => !isOwnMessage(message)).length;\n if (replies) setUnread((count) => count + replies);\n }, [messages, isOpen]);\n\n useEffect(() => {\n if (!isOpen || article) return;\n setUnread(0);\n listRef.current?.scrollTo?.({ top: listRef.current.scrollHeight });\n }, [isOpen, messages, article]);\n\n const open = () => {\n setWindowState('open');\n onOpen?.();\n };\n\n const close = () => {\n setWindowState('closed');\n onClose?.();\n };\n\n /** Drop a finished conversation so the next message opens a new ticket */\n const startOver = () => {\n setDraft('');\n setFiles([]);\n setFileError(null);\n setUnread(0);\n seenCount.current = 0;\n setSavedContactRequests([]);\n reset();\n };\n\n const submit = async () => {\n const content = draft;\n const pending = files;\n if (!content.trim() && pending.length === 0) return;\n setDraft('');\n setFiles([]);\n setFileError(null);\n const message = await send(content, pending);\n if (message) {\n onSend?.(message);\n } else {\n setDraft(content);\n setFiles(pending);\n }\n };\n\n const handleFiles = (event: ChangeEvent<HTMLInputElement>) => {\n const { accepted, problem } = pickAttachments(\n files,\n Array.from(event.target.files ?? [])\n );\n setFiles(accepted);\n setFileError(problem);\n event.target.value = '';\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n void submit();\n }\n };\n\n return (\n <div\n className={cn(\n 'fixed bottom-4 z-50 flex flex-col gap-3',\n left ? 'left-4 items-start' : 'right-4 items-end'\n )}\n >\n {isOpen && (\n <section\n role=\"dialog\"\n aria-label={title}\n className={cn(\n 'max-w-[calc(100vw-2rem)] max-h-[calc(100vh-6rem)] flex flex-col bg-background rounded-2xl shadow-lg border border-border-100 overflow-hidden transition-[width,height]',\n expanded ? 'w-[720px] h-[615px]' : 'w-[360px] h-[520px]'\n )}\n >\n {article ? (\n <SupportChatArticleView\n articleId={article.id}\n initialTitle={article.title}\n loadArticle={loadArticle}\n expanded={expanded}\n onToggleExpanded={() => setExpanded((value) => !value)}\n onBack={closeArticle}\n externalUrl={faqArticleUrl?.(article.id)}\n accentStyle={accentStyle}\n />\n ) : (\n <>\n <header\n className=\"flex items-center justify-between px-4 py-3 bg-primary-500 text-white\"\n style={accentStyle}\n >\n <Text size=\"md\" weight=\"semibold\" color=\"\">\n {title}\n </Text>\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"raw\"\n aria-label=\"Minimizar\"\n onClick={() => setWindowState('minimized')}\n className=\"p-1 rounded hover:bg-white/20\"\n >\n <MinusIcon size={18} />\n </Button>\n <Button\n variant=\"raw\"\n aria-label=\"Fechar\"\n onClick={close}\n className=\"p-1 rounded hover:bg-white/20\"\n >\n <XIcon size={18} />\n </Button>\n </div>\n </header>\n\n <div ref={listRef} className=\"flex-1 overflow-y-auto px-4 py-3\">\n {messages.length === 0 ? (\n <Text size=\"sm\" className=\"text-text-500 text-center mt-8\">\n Olá! Como podemos te ajudar?\n </Text>\n ) : (\n messages.map((message) => (\n <div key={message.id}>\n <MessageBubble\n message={message}\n faqArticleUrl={faqArticleUrl}\n onOpenArticle={openArticle}\n />\n {updateContact &&\n message.id === contactRequest?.id &&\n message.contactRequest && (\n <SupportChatContactForm\n fields={message.contactRequest}\n accentColor={theme?.primaryColor}\n onSubmit={async (contact) => {\n const ok = await updateContact(contact);\n if (ok) {\n setSavedContactRequests((ids) => [\n ...ids,\n message.id,\n ]);\n }\n return ok;\n }}\n />\n )}\n </div>\n ))\n )}\n </div>\n\n {(fileError || error) && (\n <Text\n size=\"xs\"\n role=\"alert\"\n className=\"px-4 py-2 text-error-700 bg-error-50\"\n >\n {fileError ?? error}\n </Text>\n )}\n\n {closed ? (\n <div className=\"flex flex-col items-center gap-2 px-4 py-3 border-t border-border-100\">\n <Text size=\"sm\" color=\"text-text-500\" className=\"text-center\">\n Este atendimento foi encerrado.\n </Text>\n <Button variant=\"link\" size=\"small\" onClick={startOver}>\n Iniciar nova conversa\n </Button>\n </div>\n ) : (\n <form\n className=\"flex flex-col gap-2 px-3 py-3 border-t border-border-100\"\n onSubmit={(event) => {\n event.preventDefault();\n void submit();\n }}\n >\n {files.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {files.map((file, index) => (\n <PendingAttachment\n key={`${file.name}-${index}`}\n file={file}\n onRemove={() =>\n setFiles((list) =>\n list.filter((_, i) => i !== index)\n )\n }\n />\n ))}\n </div>\n )}\n <div className=\"flex items-end gap-2\">\n <input\n ref={fileInputRef}\n type=\"file\"\n accept={SUPPORT_CHAT_ACCEPT}\n multiple\n hidden\n data-testid=\"support-chat-file-input\"\n onChange={handleFiles}\n />\n <Button\n variant=\"raw\"\n aria-label=\"Anexar arquivo\"\n disabled={\n sending ||\n awaitingContact ||\n files.length >= SUPPORT_CHAT_MAX_ATTACHMENTS\n }\n onClick={() => fileInputRef.current?.click()}\n className=\"p-2 rounded-full text-text-600 hover:bg-background-100 disabled:opacity-50\"\n >\n <PaperclipIcon size={20} />\n </Button>\n <textarea\n aria-label=\"Mensagem\"\n placeholder={\n awaitingContact\n ? 'Preencha seus dados acima'\n : 'Digite sua mensagem'\n }\n disabled={awaitingContact}\n rows={1}\n value={draft}\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={handleKeyDown}\n className=\"flex-1 resize-none rounded-xl border border-border-300 px-3 py-2 text-sm text-text-900 focus:outline-none focus:border-primary-500 max-h-28\"\n />\n <Button\n variant=\"raw\"\n type=\"submit\"\n aria-label=\"Enviar\"\n disabled={\n sending ||\n awaitingContact ||\n (!draft.trim() && files.length === 0)\n }\n className=\"p-2 rounded-full bg-primary-500 text-white disabled:opacity-50\"\n style={accentStyle}\n >\n <PaperPlaneTiltIcon size={18} />\n </Button>\n </div>\n </form>\n )}\n </>\n )}\n </section>\n )}\n\n {!isOpen && (\n <Button\n variant=\"raw\"\n aria-label={`Abrir ${title.toLowerCase()}`}\n onClick={open}\n className=\"relative w-14 h-14 rounded-full bg-primary-500 text-white shadow-lg flex items-center justify-center hover:brightness-110\"\n style={accentStyle}\n >\n <ChatCircleDotsIcon size={28} />\n {unread > 0 && (\n <Text\n as=\"span\"\n size=\"xs\"\n color=\"text-white\"\n aria-label={formatNewMessagesLabel(unread)}\n className=\"absolute -top-1 -right-1 min-w-5 h-5 px-1 rounded-full bg-error-500 flex items-center justify-center\"\n >\n {unread}\n </Text>\n )}\n </Button>\n )}\n </div>\n );\n};\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n SUPPORT_CHAT_AUTHOR,\n SUPPORT_CHAT_GUEST_SENDER_ID,\n SUPPORT_CHAT_STATUS,\n type SupportChatAdapter,\n type SupportChatContact,\n type SupportChatEvent,\n type SupportChatMessage,\n type SupportChatTicket,\n type SupportChatUser,\n} from '../types/supportChat';\n\nexport interface UseSupportChatConfig {\n adapter: SupportChatAdapter;\n channel: string;\n institutionId?: string;\n user?: SupportChatUser;\n}\n\nexport interface UseSupportChatReturn {\n ticket: SupportChatTicket | null;\n messages: SupportChatMessage[];\n sending: boolean;\n error: string | null;\n /** Ticket resolved: no more messages can be sent */\n closed: boolean;\n /** Send a message (text and/or files); the first one opens the ticket */\n send: (content: string, files?: File[]) => Promise<SupportChatMessage | null>;\n /**\n * Save the contact typed in the form shown under the AI request. Resolves\n * false (and sets `error`) when it fails. Undefined when the adapter cannot\n * save it.\n */\n updateContact?: (contact: SupportChatContact) => Promise<boolean>;\n /** Forget the current conversation */\n reset: () => void;\n}\n\nlet pendingSequence = 0;\n\n/**\n * Message shown right away while it is being sent (optimistic rendering).\n * Images use local object URLs, released when the real message arrives.\n */\nconst buildPendingMessage = (\n content: string,\n files: File[],\n user?: SupportChatUser\n): SupportChatMessage => {\n pendingSequence += 1;\n return {\n id: `pending-${pendingSequence}`,\n senderId: SUPPORT_CHAT_GUEST_SENDER_ID,\n senderName: user?.name || 'Você',\n senderPhoto: null,\n senderRole: user?.email\n ? SUPPORT_CHAT_AUTHOR.USER\n : SUPPORT_CHAT_AUTHOR.GUEST,\n content,\n messageType: 'text',\n createdAt: new Date().toISOString(),\n attachments: files.map((file, index) => ({\n id: `pending-${pendingSequence}-${index}`,\n url: URL.createObjectURL(file),\n fileName: file.name,\n mimeType: file.type,\n size: file.size,\n })),\n pending: true,\n };\n};\n\nconst releasePending = (message: SupportChatMessage) =>\n message.attachments.forEach((item) => URL.revokeObjectURL(item.url));\n\nconst getErrorMessage = (error: unknown) =>\n error instanceof Error ? error.message : 'Não foi possível enviar a mensagem';\n\n/** Add or replace a message, keeping the list ordered by arrival */\nconst upsertMessage = (\n list: SupportChatMessage[],\n message: SupportChatMessage\n): SupportChatMessage[] =>\n list.some((item) => item.id === message.id)\n ? list.map((item) => (item.id === message.id ? message : item))\n : [...list, message];\n\n/**\n * State of a support conversation on top of a SupportChatAdapter.\n * The REST-like calls write; the subscription only applies the events,\n * so a message received both ways is stored once.\n */\nexport const useSupportChat = ({\n adapter,\n channel,\n institutionId,\n user,\n}: UseSupportChatConfig): UseSupportChatReturn => {\n const [ticket, setTicket] = useState<SupportChatTicket | null>(null);\n const [messages, setMessages] = useState<SupportChatMessage[]>([]);\n const [sending, setSending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const sendingRef = useRef(false);\n\n const ticketId = ticket?.id;\n\n useEffect(() => {\n if (!ticketId) return;\n\n const handleEvent = (event: SupportChatEvent) => {\n switch (event.type) {\n case 'new_message':\n setMessages((list) => upsertMessage(list, event.payload.message));\n break;\n case 'message_updated':\n setMessages((list) =>\n list.map((item) =>\n item.id === event.payload.messageId\n ? { ...item, content: event.payload.content }\n : item\n )\n );\n break;\n case 'ticket_updated': {\n const { ticketId: id, ...rest } = event.payload;\n setTicket((current) =>\n current?.id === id ? { ...current, ...rest } : current\n );\n break;\n }\n case 'error':\n setError(event.payload.message);\n break;\n }\n };\n\n return adapter.subscribe(ticketId, handleEvent);\n }, [adapter, ticketId]);\n\n const send = useCallback(\n async (content: string, files: File[] = []) => {\n const text = content.trim();\n if ((!text && files.length === 0) || sendingRef.current) return null;\n\n sendingRef.current = true;\n setSending(true);\n setError(null);\n\n const pending = buildPendingMessage(text, files, user);\n setMessages((list) => [...list, pending]);\n // Swap the optimistic message for the confirmed ones\n const confirm = (confirmed: SupportChatMessage[]) =>\n setMessages((list) =>\n confirmed.reduce(\n upsertMessage,\n list.filter((item) => item.id !== pending.id)\n )\n );\n\n try {\n if (ticketId) {\n const message = await adapter.sendMessage(ticketId, text, files);\n confirm([message]);\n return message;\n }\n\n const started = await adapter.startConversation({\n message: text,\n channel,\n institutionId,\n requesterName: user?.name,\n email: user?.email,\n files,\n });\n setTicket(started.ticket);\n confirm(started.messages);\n return started.messages[started.messages.length - 1] ?? null;\n } catch (sendError) {\n setMessages((list) => list.filter((item) => item.id !== pending.id));\n setError(getErrorMessage(sendError));\n return null;\n } finally {\n releasePending(pending);\n sendingRef.current = false;\n setSending(false);\n }\n },\n [adapter, channel, institutionId, ticketId, user]\n );\n\n const updateContact = useCallback(\n async (contact: SupportChatContact) => {\n if (!ticketId || !adapter.updateContact) return false;\n setError(null);\n try {\n await adapter.updateContact(ticketId, contact);\n return true;\n } catch (contactError) {\n setError(\n (contactError as { response?: { status?: number } } | null)?.response\n ?.status === 409\n ? 'O e-mail deste atendimento já foi informado'\n : 'Não foi possível salvar seus dados. Tente de novo.'\n );\n return false;\n }\n },\n [adapter, ticketId]\n );\n\n const reset = useCallback(() => {\n setTicket(null);\n setMessages([]);\n setError(null);\n }, []);\n\n return {\n ticket,\n messages,\n sending,\n error,\n closed: ticket?.status === SUPPORT_CHAT_STATUS.FECHADO,\n send,\n updateContact: adapter.updateContact ? updateContact : undefined,\n reset,\n };\n};\n","import { useState, type FormEvent } from 'react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport Input from '../Input/Input';\nimport type {\n SupportChatContact,\n SupportChatContactField,\n} from '../../types/supportChat';\n\nexport interface SupportChatContactFormProps {\n /** Fields the AI asked for, in order (one step each) */\n fields: SupportChatContactField[];\n /** Saves the contact; resolves true when it worked */\n onSubmit: (contact: SupportChatContact) => Promise<boolean>;\n /** Accent color of the theme, for the button */\n accentColor?: string;\n}\n\nconst STEPS: Record<\n SupportChatContactField,\n { label: string; type: string; autoComplete: string; placeholder: string }\n> = {\n name: {\n label: 'Seu nome',\n type: 'text',\n autoComplete: 'name',\n placeholder: 'Como podemos te chamar?',\n },\n email: {\n label: 'Seu e-mail',\n type: 'email',\n autoComplete: 'email',\n placeholder: 'nome@exemplo.com',\n },\n};\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/** Problem with a typed value, or null when it can be sent */\nconst validate = (\n field: SupportChatContactField,\n value: string\n): string | null => {\n if (field === 'name') {\n return value.length >= 2 ? null : 'Digite seu nome';\n }\n return EMAIL_PATTERN.test(value) ? null : 'Digite um e-mail válido';\n};\n\n/**\n * Step-by-step form for the contact the AI asks on the first contact (name,\n * then e-mail), shown under the AI message like the Zendesk pre-chat form.\n * The values go to the backend as fields, not as a chat message. Once sent,\n * it shows what was saved.\n */\nexport const SupportChatContactForm = ({\n fields,\n onSubmit,\n accentColor,\n}: SupportChatContactFormProps) => {\n const [step, setStep] = useState(0);\n const [values, setValues] = useState<Record<string, string>>({});\n const [problem, setProblem] = useState<string | null>(null);\n const [saving, setSaving] = useState(false);\n const [saved, setSaved] = useState(false);\n\n const field = fields[step];\n const config = STEPS[field];\n const value = values[field] ?? '';\n const isLast = step === fields.length - 1;\n\n if (saved) {\n return (\n <div\n data-testid=\"support-chat-contact-saved\"\n className=\"mb-3 ml-1 flex max-w-[85%] flex-col gap-1 rounded-2xl border border-border-100 px-4 py-3\"\n >\n {fields.map((item) => (\n <Text key={item} size=\"xs\" color=\"text-text-600\">\n {STEPS[item].label}:{' '}\n <Text as=\"span\" size=\"xs\" weight=\"semibold\" color=\"text-text-900\">\n {values[item]}\n </Text>\n </Text>\n ))}\n </div>\n );\n }\n\n const advance = async (event: FormEvent) => {\n event.preventDefault();\n const typed = field === 'email' ? value.trim().toLowerCase() : value.trim();\n const error = validate(field, typed);\n setProblem(error);\n if (error) return;\n\n const next = { ...values, [field]: typed };\n setValues(next);\n if (!isLast) {\n setStep(step + 1);\n return;\n }\n\n setSaving(true);\n const ok = await onSubmit({\n ...(next.name ? { requesterName: next.name } : {}),\n ...(next.email ? { email: next.email } : {}),\n });\n setSaving(false);\n if (ok) setSaved(true);\n };\n\n const inputId = `support-chat-contact-${field}`;\n\n return (\n <form\n aria-label=\"Seus dados\"\n noValidate\n onSubmit={(event) => void advance(event)}\n className=\"mb-3 ml-1 flex max-w-[85%] flex-col gap-3 rounded-2xl border border-border-100 px-4 py-3\"\n >\n <Input\n id={inputId}\n key={field}\n label={config.label}\n variant=\"rounded\"\n size=\"small\"\n type={config.type}\n autoComplete={config.autoComplete}\n placeholder={config.placeholder}\n value={value}\n autoFocus\n disabled={saving}\n errorMessage={problem ?? undefined}\n onChange={(event) =>\n setValues((current) => ({ ...current, [field]: event.target.value }))\n }\n />\n <div className=\"flex items-center justify-between\">\n <Text size=\"xs\" color=\"text-text-500\">\n {fields.length > 1 ? `${step + 1} de ${fields.length}` : ''}\n </Text>\n <Button\n type=\"submit\"\n size=\"small\"\n disabled={saving}\n style={accentColor ? { backgroundColor: accentColor } : undefined}\n >\n {isLast ? 'Enviar' : 'Avançar'}\n </Button>\n </div>\n </form>\n );\n};\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport {\n ArrowLeftIcon,\n ArrowSquareOutIcon,\n ArrowsInSimpleIcon,\n ArrowsOutSimpleIcon,\n} from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport { ArticleContent } from '../ArticleContent/ArticleContent';\nimport { embedVideoLinks } from '../../utils/videoEmbed';\nimport type { SupportChatArticle } from '../../types/supportChat';\nimport { cn } from '../../utils/utils';\n\nexport interface SupportChatArticleViewProps {\n /** Article to show */\n articleId: string;\n /** Title known from the chat, shown while the article loads */\n initialTitle?: string;\n /** Loads the article (adapter.getArticle bound to the institution) */\n loadArticle: (articleId: string) => Promise<SupportChatArticle>;\n /** Whether the chat window is in the wide mode */\n expanded: boolean;\n onToggleExpanded: () => void;\n /** Back to the conversation (it is kept as it was) */\n onBack: () => void;\n /** Page of the article in the help center, for \"Abrir em nova aba\" */\n externalUrl?: string;\n /** Header background (theme) */\n accentStyle?: { backgroundColor: string };\n}\n\nconst formatUpdatedAt = (iso: string) =>\n new Date(iso).toLocaleDateString('pt-BR', {\n day: '2-digit',\n month: '2-digit',\n year: 'numeric',\n });\n\n/**\n * FAQ article read inside the support chat, like the Zendesk messenger: the\n * conversation is replaced by the article (no new tab), with back,\n * expand/reduce and open-in-new-tab controls. The big title moves to the\n * header once it scrolls out of view. YouTube/Vimeo links of the article\n * become players.\n */\nexport const SupportChatArticleView = ({\n articleId,\n initialTitle,\n loadArticle,\n expanded,\n onToggleExpanded,\n onBack,\n externalUrl,\n accentStyle,\n}: SupportChatArticleViewProps) => {\n const [article, setArticle] = useState<SupportChatArticle | null>(null);\n const [failed, setFailed] = useState(false);\n const [titleHidden, setTitleHidden] = useState(false);\n const titleRef = useRef<HTMLHeadingElement>(null);\n\n useEffect(() => {\n let active = true;\n setArticle(null);\n setFailed(false);\n loadArticle(articleId)\n .then((loaded) => {\n if (active) setArticle(loaded);\n })\n .catch(() => {\n if (active) setFailed(true);\n });\n return () => {\n active = false;\n };\n }, [articleId, loadArticle]);\n\n // The big title leaves the screen → show it in the header\n useEffect(() => {\n const title = titleRef.current;\n if (!title || typeof IntersectionObserver === 'undefined') {\n return undefined;\n }\n const observer = new IntersectionObserver(([entry]) =>\n setTitleHidden(!entry.isIntersecting)\n );\n observer.observe(title);\n return () => observer.disconnect();\n }, [article]);\n\n const html = useMemo(\n () => (article ? embedVideoLinks(article.body) : ''),\n [article]\n );\n const title = article?.title ?? initialTitle ?? 'Artigo';\n\n return (\n <div\n data-testid=\"support-chat-article\"\n className=\"flex min-h-0 flex-1 flex-col\"\n >\n <header\n className=\"flex items-center gap-1 px-2 py-2 bg-primary-500 text-white\"\n style={accentStyle}\n >\n <Button\n variant=\"raw\"\n aria-label=\"Voltar para a janela de mensagens\"\n onClick={onBack}\n className=\"p-1 rounded hover:bg-white/20\"\n >\n <ArrowLeftIcon size={18} />\n </Button>\n <Text\n as=\"span\"\n size=\"sm\"\n weight=\"semibold\"\n color=\"\"\n aria-hidden={!titleHidden || undefined}\n className={cn(\n 'min-w-0 flex-1 truncate transition-opacity',\n titleHidden ? 'opacity-100' : 'opacity-0'\n )}\n >\n {title}\n </Text>\n <Button\n variant=\"raw\"\n aria-label={expanded ? 'Reduzir artigo' : 'Expandir artigo'}\n onClick={onToggleExpanded}\n className=\"p-1 rounded hover:bg-white/20\"\n >\n {expanded ? (\n <ArrowsInSimpleIcon size={18} />\n ) : (\n <ArrowsOutSimpleIcon size={18} />\n )}\n </Button>\n {externalUrl && (\n <a\n href={externalUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n aria-label=\"Abrir em nova aba\"\n className=\"p-1 rounded hover:bg-white/20\"\n >\n <ArrowSquareOutIcon size={18} />\n </a>\n )}\n </header>\n\n <div className=\"flex-1 overflow-y-auto px-5 py-4\">\n <Text\n as=\"h2\"\n ref={titleRef}\n size=\"xl\"\n weight=\"bold\"\n color=\"text-text-950\"\n >\n {title}\n </Text>\n {article?.updatedAt && (\n <Text size=\"xs\" color=\"text-text-500\" className=\"mt-1\">\n Atualizado em {formatUpdatedAt(article.updatedAt)}\n </Text>\n )}\n\n <div className=\"mt-4\">\n {failed && (\n <Text size=\"sm\" role=\"alert\" color=\"text-error-700\">\n Não foi possível abrir o artigo.\n </Text>\n )}\n {!failed && !article && (\n <Text size=\"sm\" color=\"text-text-500\">\n Carregando…\n </Text>\n )}\n {article && <ArticleContent html={html} />}\n </div>\n </div>\n </div>\n );\n};\n","import { useEffect, useState, type ReactNode } from 'react';\nimport {\n FileIcon,\n FilePdfIcon,\n FileVideoIcon,\n XIcon,\n} from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport {\n SUPPORT_CHAT_ATTACHMENT_RULES,\n SUPPORT_CHAT_MAX_ATTACHMENTS,\n type SupportChatAttachment,\n} from '../../types/supportChat';\n\n/** Value of the `accept` attribute of the file picker */\nexport const SUPPORT_CHAT_ACCEPT = Object.keys(\n SUPPORT_CHAT_ATTACHMENT_RULES\n).join(',');\n\nconst isImage = (mimeType?: string) => Boolean(mimeType?.startsWith('image/'));\nconst isVideo = (mimeType?: string) => Boolean(mimeType?.startsWith('video/'));\n\nconst formatSize = (bytes: number) => `${Math.round(bytes / (1024 * 1024))} MB`;\n\n/**\n * Keep the files the backend accepts (type, size and count) and say why the\n * others were left out.\n * @param current - Files already picked\n * @param picked - Files just chosen\n * @returns The accepted files and the last problem found, if any\n */\nexport const pickAttachments = (current: File[], picked: File[]) => {\n const accepted = [...current];\n let problem: string | null = null;\n for (const file of picked) {\n const maxBytes = SUPPORT_CHAT_ATTACHMENT_RULES[file.type];\n if (!maxBytes) {\n problem =\n 'Envie imagens (JPG, PNG, GIF, WEBP), PDF ou vídeo (MP4, WEBM, MOV)';\n } else if (file.size > maxBytes) {\n problem = `${file.name} passa de ${formatSize(maxBytes)}`;\n } else if (accepted.length >= SUPPORT_CHAT_MAX_ATTACHMENTS) {\n problem = `Máximo de ${SUPPORT_CHAT_MAX_ATTACHMENTS} arquivos por mensagem`;\n } else {\n accepted.push(file);\n }\n }\n return { accepted, problem };\n};\n\nconst FileTypeIcon = ({ mimeType }: { mimeType?: string }) => {\n if (mimeType === 'application/pdf') return <FilePdfIcon size={18} />;\n if (isVideo(mimeType)) return <FileVideoIcon size={18} />;\n return <FileIcon size={18} />;\n};\n\n/** Files of a message: images and videos inline, other files as links */\nexport const AttachmentList = ({\n items,\n}: {\n items: SupportChatAttachment[];\n}) => (\n <div className=\"flex flex-wrap gap-2 first:mt-0 mt-1\">\n {items.map((item) => {\n if (isImage(item.mimeType)) {\n return (\n <a key={item.id} href={item.url} target=\"_blank\" rel=\"noreferrer\">\n <img\n src={item.url}\n alt={item.fileName}\n className=\"max-w-[200px] max-h-[200px] rounded-lg object-cover\"\n />\n </a>\n );\n }\n if (isVideo(item.mimeType)) {\n return (\n <video\n key={item.id}\n src={item.url}\n controls\n preload=\"metadata\"\n aria-label={item.fileName}\n className=\"max-w-[240px] max-h-[200px] rounded-lg bg-black\"\n />\n );\n }\n return (\n <a\n key={item.id}\n href={item.url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"flex items-center gap-1 underline\"\n >\n <FileTypeIcon mimeType={item.mimeType} />\n <Text as=\"span\" size=\"sm\" color=\"\">\n {item.fileName}\n </Text>\n </a>\n );\n })}\n </div>\n);\n\n/** A file picked but not sent yet: thumbnail for images, name for the rest */\nexport const PendingAttachment = ({\n file,\n onRemove,\n}: {\n file: File;\n onRemove: () => void;\n}) => {\n const image = isImage(file.type);\n const [url, setUrl] = useState('');\n useEffect(() => {\n if (!image) return undefined;\n const objectUrl = URL.createObjectURL(file);\n setUrl(objectUrl);\n return () => URL.revokeObjectURL(objectUrl);\n }, [file, image]);\n\n return (\n <div className=\"relative\">\n {image ? (\n url && (\n <img\n src={url}\n alt={file.name}\n className=\"w-14 h-14 rounded-lg object-cover border border-border-100\"\n />\n )\n ) : (\n <div className=\"flex h-14 max-w-[160px] items-center gap-1 rounded-lg border border-border-100 px-2 text-text-700\">\n <FileTypeIcon mimeType={file.type} />\n <Text as=\"span\" size=\"xs\" className=\"truncate\">\n {file.name}\n </Text>\n </div>\n )}\n <Button\n variant=\"raw\"\n aria-label={`Remover ${file.name}`}\n onClick={onRemove}\n className=\"absolute -top-1 -right-1 w-5 h-5 rounded-full bg-text-700 text-white flex items-center justify-center\"\n >\n <XIcon size={12} />\n </Button>\n </div>\n );\n};\n\n/** http(s) addresses inside a message */\nconst URL_PATTERN = /https?:\\/\\/[^\\s<>\"]+/g;\n\n/** Punctuation that ends a sentence, not the address */\nconst TRAILING_PUNCTUATION = /[.,;:!?)\\]]+$/;\n\n/** A FAQ article page: `/artigo/<uuid>` */\nconst ARTICLE_PATH =\n /\\/artigo\\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i;\n\n/**\n * Message text with its addresses as links. A link to a FAQ article opens in\n * the chat (when `onOpenArticle` is given); the others open a new tab.\n */\nexport const MessageText = ({\n text,\n onOpenArticle,\n}: {\n text: string;\n onOpenArticle?: (articleId: string) => void;\n}) => {\n const parts: ReactNode[] = [];\n let cursor = 0;\n for (const match of text.matchAll(URL_PATTERN)) {\n const raw = match[0];\n const url = raw.replace(TRAILING_PUNCTUATION, '');\n const start = match.index ?? 0;\n if (start > cursor) parts.push(text.slice(cursor, start));\n\n const articleId = onOpenArticle ? ARTICLE_PATH.exec(url)?.[1] : undefined;\n parts.push(\n <a\n key={`${start}-${url}`}\n href={url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"underline break-all\"\n onClick={\n articleId\n ? (event) => {\n event.preventDefault();\n onOpenArticle?.(articleId);\n }\n : undefined\n }\n >\n {url}\n </a>\n );\n cursor = start + url.length;\n }\n if (cursor < text.length) parts.push(text.slice(cursor));\n\n return (\n <Text size=\"sm\" color=\"\" className=\"whitespace-pre-wrap\">\n {parts}\n </Text>\n );\n};\n","import { useMemo } from 'react';\nimport { SupportChatWidget } from './SupportChatWidget';\nimport { createGuestSupportChatAdapter } from './guestSupportChatAdapter';\nimport { useSupportFeatureFlag } from '../../hooks/useSupportFeatureFlag';\nimport { useAppStore } from '../../store/appStore';\nimport { useAuthStore } from '../../store/authStore';\nimport { SupportType, type SupportApiClient } from '../../types/support';\nimport type {\n SupportChatHttpClient,\n SupportChatTheme,\n} from '../../types/supportChat';\n\nexport interface NativeSupportChatProps {\n /** Client of the backend API (the app axios instance fits as it is) */\n apiClient: SupportApiClient & SupportChatHttpClient;\n /** App the chat runs in, sent with the ticket (e.g. \"aluno\", \"login\") */\n channel: string;\n /**\n * Extra condition of the app to show the chat, on top of the SUPPORT flag\n * (e.g. logged in and with the support module). Default: true.\n */\n enabled?: boolean;\n /**\n * Link of a FAQ article. Default: the FAQ address of the SUPPORT flag\n * (`<faqUrl>/artigo/<id>`).\n */\n faqArticleUrl?: (articleId: string) => string;\n /** Support WebSocket; without it the chat polls the REST API */\n wsUrl?: string;\n theme?: SupportChatTheme;\n}\n\n/**\n * Native support chat ready to drop in an app: reads the SUPPORT feature flag\n * of the institution (from the app store), the logged user (from the auth\n * store) and talks to the support API by itself.\n *\n * Shown only when the institution uses the native support with the internal\n * support and its chat switched on in the backoffice, once the flag is read,\n * and when `enabled`. With Zendesk it renders nothing (the app loads the\n * Zendesk widget). A logged user opens the ticket with their name and e-mail;\n * a visitor gives them to the AI on the first contact.\n *\n * @example\n * <NativeSupportChat apiClient={api} channel=\"aluno\" enabled={hasSupport} />\n */\nexport const NativeSupportChat = ({\n apiClient,\n channel,\n enabled = true,\n faqArticleUrl,\n wsUrl,\n theme,\n}: NativeSupportChatProps) => {\n const { institutionId } = useAppStore();\n const userName = useAuthStore(\n (state) => state.user?.name ?? state.sessionInfo?.userName\n );\n const userEmail = useAuthStore((state) => state.user?.email);\n const flag = useSupportFeatureFlag({ apiClient });\n const adapter = useMemo(\n () => createGuestSupportChatAdapter({ apiClient, wsUrl }),\n [apiClient, wsUrl]\n );\n\n const user =\n userName || userEmail ? { name: userName, email: userEmail } : undefined;\n\n return (\n <SupportChatWidget\n adapter={adapter}\n channel={channel}\n institutionId={institutionId || undefined}\n user={user}\n theme={theme}\n faqArticleUrl={faqArticleUrl ?? flag.faqArticleUrl}\n flags={{\n enabled: enabled && !flag.loading && flag.isNative && flag.chatEnabled,\n supportType: flag.isNative ? SupportType.NATIVE : SupportType.ZENDESK,\n }}\n />\n );\n};\n","import { isSupportTicketOrigin } from '../../types/support';\nimport {\n SUPPORT_CHAT_AUTHOR,\n SUPPORT_CHAT_GUEST_SENDER_ID,\n SUPPORT_CHAT_STATUS,\n type SupportChatAdapter,\n type SupportChatAnswerApi,\n type SupportChatArticle,\n type SupportChatEvent,\n type SupportChatHttpClient,\n type SupportChatMessage,\n} from '../../types/supportChat';\n\nexport interface GuestSupportChatAdapterOptions {\n /** Client of the backend API (e.g. the app axios instance) */\n apiClient: SupportChatHttpClient;\n /**\n * Support WebSocket, including ?channel=support (e.g.\n * wss://bff-hml-chat.analyticaensino.com.br?channel=support).\n * Without it the adapter only polls the REST API.\n */\n wsUrl?: string;\n /** Polling interval while the socket is not connected, in ms */\n pollIntervalMs?: number;\n}\n\nconst GUEST_TOKEN_HEADER = 'x-support-guest-token';\n\nconst senderNameFor = (role: string, requesterName: string) => {\n if (role === SUPPORT_CHAT_AUTHOR.AGENT) return 'Suporte';\n if (role === SUPPORT_CHAT_AUTHOR.AI) return 'Assistente';\n return requesterName;\n};\n\n/** REST answer → widget message */\nexport const fromSupportAnswer = (\n answer: SupportChatAnswerApi,\n requesterName: string\n): SupportChatMessage => ({\n id: answer.id,\n senderId:\n answer.authorType === SUPPORT_CHAT_AUTHOR.GUEST\n ? SUPPORT_CHAT_GUEST_SENDER_ID\n : answer.authorType.toLowerCase(),\n senderName: senderNameFor(answer.authorType, requesterName),\n senderPhoto: null,\n senderRole: answer.authorType,\n content: answer.answer,\n messageType: 'text',\n createdAt: answer.createdAt,\n attachments: answer.attachments ?? [],\n faq:\n answer.faqArticleId && answer.faqArticleTitle\n ? { id: answer.faqArticleId, title: answer.faqArticleTitle }\n : null,\n contactRequest: answer.contactRequest ?? null,\n});\n\n/**\n * Support chat adapter for visitors (no login), on top of the support API:\n * - POST /support/external opens the ticket and returns the guest token;\n * - GET/POST /support/guest/:id/answer read and send, with the token header;\n * - the chat WebSocket (?channel=support&roomId=&token=) pushes the events,\n * and REST polling covers the time the socket is down.\n */\nexport const createGuestSupportChatAdapter = ({\n apiClient,\n wsUrl,\n pollIntervalMs = 3000,\n}: GuestSupportChatAdapterOptions): SupportChatAdapter => {\n const sessions = new Map<string, { token: string; name: string }>();\n /**\n * Ticket opening text written by the adapter (an image-only start still\n * needs a description). It is the ticket description for the agents, not\n * something the visitor typed, so the chat does not show it.\n */\n const hiddenIds = new Set<string>();\n const isVisible = (message: SupportChatMessage) => !hiddenIds.has(message.id);\n\n const headersFor = (ticketId: string) => ({\n [GUEST_TOKEN_HEADER]: sessions.get(ticketId)?.token ?? '',\n });\n const nameFor = (ticketId: string) =>\n sessions.get(ticketId)?.name ?? 'Visitante';\n\n const listMessages = async (ticketId: string) => {\n const { data } = await apiClient.get<{ data: SupportChatAnswerApi[] }>(\n `/support/guest/${ticketId}/answer`,\n { headers: headersFor(ticketId) }\n );\n return data.data\n .map((answer) => fromSupportAnswer(answer, nameFor(ticketId)))\n .filter(isVisible);\n };\n\n /**\n * Upload each file straight to the storage: ask the backend for a signed\n * URL, then PUT the file there. Returns what the message needs.\n */\n const uploadFiles = (ticketId: string, files: File[]) =>\n Promise.all(\n files.map(async (file) => {\n const { data } = await apiClient.post<{\n data: { uploadUrl: string; key: string; contentType: string };\n }>(\n `/support/guest/${ticketId}/upload-url`,\n { fileName: file.name, fileSize: file.size, mimeType: file.type },\n { headers: headersFor(ticketId) }\n );\n const { uploadUrl, key, contentType } = data.data;\n const response = await fetch(uploadUrl, {\n method: 'PUT',\n headers: { 'Content-Type': contentType },\n body: file,\n });\n if (!response.ok) {\n throw new Error(`Não foi possível enviar ${file.name}`);\n }\n return {\n key,\n fileName: file.name,\n mimeType: contentType,\n size: file.size,\n };\n })\n );\n\n const sendMessage = async (\n ticketId: string,\n content: string,\n files: File[] = []\n ) => {\n const attachments = await uploadFiles(ticketId, files);\n const { data } = await apiClient.post<{ data: SupportChatAnswerApi }>(\n `/support/guest/${ticketId}/answer`,\n { answer: content, attachments },\n { headers: headersFor(ticketId) }\n );\n return fromSupportAnswer(data.data, nameFor(ticketId));\n };\n\n return {\n async startConversation({\n message,\n channel,\n institutionId,\n requesterName,\n email,\n files = [],\n }) {\n // The ticket needs a description; an image-only start uses the names\n const description = message || files.map((file) => file.name).join(', ');\n const { data } = await apiClient.post<{\n data: { id: string; guestToken: string };\n }>('/support/external', {\n description,\n ...(institutionId ? { institutionId } : {}),\n ...(requesterName ? { requesterName } : {}),\n ...(email ? { email } : {}),\n // The app the chat runs in, when it is one the API knows\n ...(isSupportTicketOrigin(channel) ? { origin: channel } : {}),\n });\n const ticketId = data.data.id;\n sessions.set(ticketId, {\n token: data.data.guestToken,\n name: requesterName || 'Visitante',\n });\n\n // The first message is saved with the ticket; read it back for its id.\n // /support/external takes no files, so they go as the next message.\n let messages = await listMessages(ticketId);\n if (!message && messages[0]) {\n hiddenIds.add(messages[0].id);\n messages = messages.slice(1);\n }\n if (files.length) messages.push(await sendMessage(ticketId, '', files));\n return {\n ticket: {\n id: ticketId,\n status: SUPPORT_CHAT_STATUS.ABERTO,\n handlerType: null,\n problemType: null,\n },\n messages,\n };\n },\n\n sendMessage,\n\n async getArticle(institutionId, articleId) {\n const { data } = await apiClient.get<{\n data: { article: SupportChatArticle };\n }>(`/help-center/${institutionId}/articles/${articleId}`);\n const { id, title, body, updatedAt } = data.data.article;\n return { id, title, body, updatedAt: updatedAt ?? null };\n },\n\n async updateContact(ticketId, contact) {\n if (!apiClient.put) {\n throw new Error('O cliente da API não envia PUT');\n }\n await apiClient.put(`/support/guest/${ticketId}`, contact, {\n headers: headersFor(ticketId),\n });\n const session = sessions.get(ticketId);\n if (session && contact.requesterName) {\n session.name = contact.requesterName;\n }\n },\n\n listMessages,\n\n subscribe(ticketId, listener) {\n // id → what the chat shows besides the text; a message seen again with\n // more of it (a stale WebSocket history may lack contactRequest/faq) is\n // delivered again so the chat replaces it\n const seen = new Map<string, string>();\n const extrasOf = (message: SupportChatMessage) =>\n JSON.stringify([message.contactRequest ?? null, message.faq ?? null]);\n const NO_EXTRAS = JSON.stringify([null, null]);\n let socket: WebSocket | null = null;\n let pollTimer: ReturnType<typeof setInterval> | null = null;\n let active = true;\n\n const deliver = (message: SupportChatMessage) => {\n if (!isVisible(message)) return;\n const extras = extrasOf(message);\n const previous = seen.get(message.id);\n // Same as before, or a poorer copy: nothing new to show\n if (previous === extras || (previous && extras === NO_EXTRAS)) return;\n seen.set(message.id, extras);\n listener({ type: 'new_message', payload: { message } });\n };\n\n const poll = async () => {\n try {\n (await listMessages(ticketId)).forEach(deliver);\n } catch {\n // Next tick tries again\n }\n };\n const startPolling = () => {\n if (pollTimer || !active) return;\n void poll();\n pollTimer = setInterval(poll, pollIntervalMs);\n };\n const stopPolling = () => {\n if (pollTimer) clearInterval(pollTimer);\n pollTimer = null;\n };\n\n const handleFrame = (raw: string) => {\n let event: { type?: string; payload?: Record<string, unknown> };\n try {\n event = JSON.parse(raw);\n } catch {\n return;\n }\n const payload = event.payload ?? {};\n if (event.type === 'history') {\n (payload.messages as SupportChatMessage[]).forEach((message) =>\n deliver({ ...message, attachments: message.attachments ?? [] })\n );\n } else if (event.type === 'new_message') {\n const message = payload.message as SupportChatMessage;\n deliver({ ...message, attachments: message.attachments ?? [] });\n } else if (\n event.type === 'message_updated' ||\n event.type === 'ticket_updated'\n ) {\n listener(event as SupportChatEvent);\n } else if (event.type === 'error') {\n listener({\n type: 'error',\n payload: {\n message: String(payload.message_text ?? payload.message ?? ''),\n },\n });\n }\n };\n\n const token = sessions.get(ticketId)?.token;\n if (wsUrl && token) {\n const url = new URL(wsUrl);\n url.searchParams.set('channel', 'support');\n url.searchParams.set('roomId', ticketId);\n url.searchParams.set('token', token);\n socket = new WebSocket(url.toString());\n socket.onopen = () => {\n stopPolling();\n socket?.send(JSON.stringify({ type: 'getInitialData' }));\n // The REST API is the source of truth: a WebSocket deployed apart\n // from it may send a history without contactRequest/faq\n void poll();\n };\n socket.onmessage = (event) => handleFrame(String(event.data));\n socket.onclose = startPolling;\n socket.onerror = startPolling;\n } else {\n startPolling();\n }\n\n return () => {\n active = false;\n stopPolling();\n if (socket) {\n socket.onclose = null;\n socket.close();\n }\n };\n },\n };\n};\n","import {\n SUPPORT_CHAT_AUTHOR,\n SUPPORT_CHAT_GUEST_SENDER_ID,\n SUPPORT_CHAT_HANDLER,\n SUPPORT_CHAT_STATUS,\n type SupportChatAdapter,\n type SupportChatEvent,\n type SupportChatMessage,\n type SupportChatTicket,\n} from '../../types/supportChat';\n\nexport interface MockSupportChatAdapterOptions {\n /** Latency of every call, in ms (default 300) */\n delayMs?: number;\n /** Delay of the automatic agent reply, in ms (default 1500) */\n replyDelayMs?: number;\n /** Text of the agent reply; null disables it */\n replyText?: string | null;\n}\n\nconst DEFAULT_REPLY =\n 'Olá! Recebemos sua mensagem e já vamos te ajudar. Este é um atendimento de demonstração.';\n\n/**\n * In-memory adapter for the support chat widget. It mirrors the backend\n * contract (statuses, authors, events) so the widget can be built and tested\n * before the real API + WebSocket adapter exists.\n */\nexport const createMockSupportChatAdapter = ({\n delayMs = 300,\n replyDelayMs = 1500,\n replyText = DEFAULT_REPLY,\n}: MockSupportChatAdapterOptions = {}): SupportChatAdapter => {\n const tickets = new Map<string, SupportChatTicket>();\n const messages = new Map<string, SupportChatMessage[]>();\n const requesters = new Map<string, { name: string; logged: boolean }>();\n const listeners = new Map<string, Set<(event: SupportChatEvent) => void>>();\n let sequence = 0;\n\n const wait = () => new Promise((resolve) => setTimeout(resolve, delayMs));\n const nextId = (prefix: string) => `${prefix}-${++sequence}`;\n\n const emit = (ticketId: string, event: SupportChatEvent) => {\n listeners.get(ticketId)?.forEach((listener) => listener(event));\n };\n\n const append = (ticketId: string, message: SupportChatMessage) => {\n messages.set(ticketId, [...(messages.get(ticketId) ?? []), message]);\n };\n\n const toAttachments = (files: File[]) =>\n files.map((file) => ({\n id: nextId('file'),\n url: URL.createObjectURL(file),\n fileName: file.name,\n mimeType: file.type,\n size: file.size,\n }));\n\n const buildRequesterMessage = (\n ticketId: string,\n content: string,\n files: File[] = []\n ): SupportChatMessage => {\n const requester = requesters.get(ticketId);\n return {\n id: nextId('msg'),\n senderId: requester?.logged ? 'user' : SUPPORT_CHAT_GUEST_SENDER_ID,\n senderName: requester?.name ?? 'Visitante',\n senderPhoto: null,\n senderRole: requester?.logged\n ? SUPPORT_CHAT_AUTHOR.USER\n : SUPPORT_CHAT_AUTHOR.GUEST,\n content,\n messageType: 'text',\n createdAt: new Date().toISOString(),\n attachments: toAttachments(files),\n };\n };\n\n const scheduleReply = (ticketId: string) => {\n if (replyText === null) return;\n setTimeout(() => {\n const ticket = tickets.get(ticketId);\n if (!ticket) return;\n\n const reply: SupportChatMessage = {\n id: nextId('msg'),\n senderId: 'agent',\n senderName: 'Suporte',\n senderPhoto: null,\n senderRole: SUPPORT_CHAT_AUTHOR.AGENT,\n content: replyText,\n messageType: 'text',\n createdAt: new Date().toISOString(),\n attachments: [],\n };\n append(ticketId, reply);\n\n const updated: SupportChatTicket = {\n ...ticket,\n status: SUPPORT_CHAT_STATUS.EM_ANDAMENTO,\n handlerType: SUPPORT_CHAT_HANDLER.HUMANO,\n };\n tickets.set(ticketId, updated);\n\n emit(ticketId, { type: 'new_message', payload: { message: reply } });\n emit(ticketId, {\n type: 'ticket_updated',\n payload: {\n ticketId,\n status: updated.status,\n handlerType: updated.handlerType,\n problemType: updated.problemType,\n },\n });\n }, replyDelayMs);\n };\n\n return {\n async getArticle(_institutionId, articleId) {\n await wait();\n return {\n id: articleId,\n title: 'Como gerar e localizar simulados',\n body:\n '<p>Monte um simulado com as matérias que quiser e acompanhe o resultado. 📝</p>' +\n '<h2>Passo a passo</h2>' +\n '<ol><li><p>No menu, abra <strong>Simulados</strong>.</p></li>' +\n '<li><p>Clique em <strong>Gerar simulado</strong> e escolha as áreas.</p></li>' +\n '<li><p>Responda e veja o resultado em <strong>Histórico</strong>.</p></li></ol>' +\n '<p><a href=\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\">Assista ao vídeo do passo a passo</a></p>' +\n '<blockquote><p>💡 Dica: refaça as questões que errou.</p></blockquote>',\n updatedAt: '2026-10-21T12:00:00.000Z',\n };\n },\n\n async startConversation({ message, requesterName, email, files }) {\n await wait();\n const ticket: SupportChatTicket = {\n id: nextId('ticket'),\n status: SUPPORT_CHAT_STATUS.ABERTO,\n handlerType: null,\n problemType: null,\n };\n tickets.set(ticket.id, ticket);\n requesters.set(ticket.id, {\n name: requesterName || 'Visitante',\n logged: Boolean(email),\n });\n\n const first = buildRequesterMessage(ticket.id, message, files);\n append(ticket.id, first);\n scheduleReply(ticket.id);\n return { ticket, messages: [first] };\n },\n\n async sendMessage(ticketId, content, files) {\n await wait();\n const ticket = tickets.get(ticketId);\n if (!ticket) throw new Error('Atendimento não encontrado');\n if (ticket.status === SUPPORT_CHAT_STATUS.FECHADO) {\n throw new Error('Este atendimento já foi encerrado');\n }\n\n const message = buildRequesterMessage(ticketId, content, files);\n append(ticketId, message);\n scheduleReply(ticketId);\n return message;\n },\n\n async listMessages(ticketId) {\n await wait();\n return [...(messages.get(ticketId) ?? [])];\n },\n\n subscribe(ticketId, listener) {\n const set = listeners.get(ticketId) ?? new Set();\n set.add(listener);\n listeners.set(ticketId, set);\n return () => {\n set.delete(listener);\n };\n },\n };\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/SupportChatWidget/SupportChatWidget.tsx","../src/hooks/useSupportChat.ts","../src/components/SupportChatWidget/SupportChatContactForm.tsx","../src/components/SupportChatWidget/SupportChatArticleView.tsx","../src/components/SupportChatWidget/supportChatContent.tsx","../src/components/SupportChatWidget/NativeSupportChat.tsx","../src/components/SupportChatWidget/guestSupportChatAdapter.ts","../src/components/SupportChatWidget/mockSupportChatAdapter.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type ChangeEvent,\n type KeyboardEvent,\n} from 'react';\nimport {\n ChatCircleDotsIcon,\n MinusIcon,\n PaperclipIcon,\n PaperPlaneTiltIcon,\n XIcon,\n} from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport { useSupportChat } from '../../hooks/useSupportChat';\nimport { SupportChatContactForm } from './SupportChatContactForm';\nimport { SupportChatArticleView } from './SupportChatArticleView';\nimport {\n AttachmentList,\n MessageText,\n PendingAttachment,\n SUPPORT_CHAT_ACCEPT,\n pickAttachments,\n} from './supportChatContent';\nimport { SupportType } from '../../types/support';\nimport {\n SUPPORT_CHAT_AUTHOR,\n SUPPORT_CHAT_MAX_ATTACHMENTS,\n type SupportChatAdapter,\n type SupportChatFlags,\n type SupportChatMessage,\n type SupportChatTheme,\n type SupportChatUser,\n} from '../../types/supportChat';\nimport { cn } from '../../utils/utils';\n\n/** Visibility of the chat window */\nexport type SupportChatWindowState = 'closed' | 'open' | 'minimized';\n\nexport interface SupportChatWidgetProps {\n /** Data layer (createMockSupportChatAdapter for now) */\n adapter: SupportChatAdapter;\n /** App the widget runs in, sent with the ticket (e.g. \"login\", \"backoffice\") */\n channel: string;\n institutionId?: string;\n /** Logged user; omit for visitors */\n user?: SupportChatUser;\n flags?: SupportChatFlags;\n theme?: SupportChatTheme;\n onOpen?: () => void;\n onSend?: (message: SupportChatMessage) => void;\n /**\n * Called when the window is closed with X. Closing only hides it: the\n * conversation stays connected and new replies count as unread.\n */\n onClose?: () => void;\n /**\n * Link of a FAQ article, e.g. `(id) => '/artigo/' + id`. With it, the\n * \"Baseado na FAQ\" line under an AI answer opens the article; without it\n * the line only names the article.\n */\n faqArticleUrl?: (articleId: string) => string;\n}\n\n/** Label read by screen readers on the unread badge of the launcher */\nconst formatNewMessagesLabel = (count: number): string =>\n count === 1 ? '1 nova mensagem' : `${count} novas mensagens`;\n\n/** The requester side of the conversation (user or visitor) */\nconst isOwnMessage = (message: SupportChatMessage) =>\n message.senderRole === SUPPORT_CHAT_AUTHOR.USER ||\n message.senderRole === SUPPORT_CHAT_AUTHOR.GUEST;\n\nconst formatTime = (createdAt: string) =>\n new Date(createdAt).toLocaleTimeString('pt-BR', {\n hour: '2-digit',\n minute: '2-digit',\n });\n\n/** \"Baseado na FAQ: <title>\" under an AI answer, linked when possible */\nconst FaqReference = ({\n faq,\n faqArticleUrl,\n onOpenArticle,\n}: {\n faq: { id: string; title: string };\n faqArticleUrl?: (articleId: string) => string;\n onOpenArticle?: (article: { id: string; title: string }) => void;\n}) => {\n const linked = !!onOpenArticle || !!faqArticleUrl;\n const label = (\n <Text\n as=\"span\"\n size=\"xs\"\n color=\"text-primary-700\"\n className={cn(linked && 'underline')}\n >\n Baseado na FAQ: {faq.title}\n </Text>\n );\n let content = label;\n if (onOpenArticle) {\n // Opens inside the chat, like the Zendesk messenger (no new tab)\n content = (\n <a\n href={faqArticleUrl?.(faq.id) ?? '#'}\n onClick={(event) => {\n event.preventDefault();\n onOpenArticle(faq);\n }}\n >\n {label}\n </a>\n );\n } else if (faqArticleUrl) {\n content = (\n <a href={faqArticleUrl(faq.id)} target=\"_blank\" rel=\"noreferrer\">\n {label}\n </a>\n );\n }\n return (\n <div data-testid=\"support-chat-faq\" className=\"mt-1 max-w-[80%]\">\n {content}\n </div>\n );\n};\n\nconst MessageBubble = ({\n message,\n faqArticleUrl,\n onOpenArticle,\n}: {\n message: SupportChatMessage;\n faqArticleUrl?: (articleId: string) => string;\n onOpenArticle?: (article: { id: string; title?: string }) => void;\n}) => {\n const own = isOwnMessage(message);\n return (\n <div\n className={cn(\n 'flex flex-col mb-3 transition-opacity',\n own ? 'items-end' : 'items-start',\n message.pending && 'opacity-60'\n )}\n aria-busy={message.pending || undefined}\n >\n {!own && (\n <Text size=\"xs\" className=\"text-text-500 mb-1\">\n {message.senderName}\n </Text>\n )}\n <div\n className={cn(\n 'max-w-[80%] rounded-2xl whitespace-pre-wrap break-words',\n // Image-only messages show the image without the colored bubble\n message.content\n ? cn(\n 'px-4 py-2',\n own\n ? 'bg-primary-500 text-white rounded-br-md'\n : 'bg-background-100 text-text-900 rounded-bl-md'\n )\n : 'text-text-900'\n )}\n >\n {message.content && (\n <MessageText\n text={message.content}\n onOpenArticle={\n onOpenArticle ? (id) => onOpenArticle({ id }) : undefined\n }\n />\n )}\n {message.attachments.length > 0 && (\n <AttachmentList items={message.attachments} />\n )}\n </div>\n {message.faq && (\n <FaqReference\n faq={message.faq}\n faqArticleUrl={faqArticleUrl}\n onOpenArticle={onOpenArticle}\n />\n )}\n <Text size=\"xs\" className=\"text-text-400 mt-1\">\n {message.pending ? 'Enviando…' : formatTime(message.createdAt)}\n </Text>\n </div>\n );\n};\n\n/**\n * Floating support chat (native support, the Zendesk replacement).\n * Renders nothing when disabled or when the institution uses Zendesk.\n */\nexport const SupportChatWidget = ({\n flags,\n ...props\n}: SupportChatWidgetProps) => {\n const hidden =\n flags?.enabled === false ||\n flags?.chat === false ||\n flags?.supportType === SupportType.ZENDESK;\n\n if (hidden) return null;\n return <SupportChatWidgetContent {...props} />;\n};\n\nconst SupportChatWidgetContent = ({\n adapter,\n channel,\n institutionId,\n user,\n theme,\n onOpen,\n onSend,\n onClose,\n faqArticleUrl,\n}: Omit<SupportChatWidgetProps, 'flags'>) => {\n const [windowState, setWindowState] =\n useState<SupportChatWindowState>('closed');\n const [draft, setDraft] = useState('');\n const [files, setFiles] = useState<File[]>([]);\n const [fileError, setFileError] = useState<string | null>(null);\n const [unread, setUnread] = useState(0);\n // FAQ article open inside the chat (the conversation stays as it was)\n const [article, setArticle] = useState<{ id: string; title?: string } | null>(\n null\n );\n const [expanded, setExpanded] = useState(false);\n const listRef = useRef<HTMLDivElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const seenCount = useRef(0);\n\n const { messages, sending, error, closed, send, updateContact, reset } =\n useSupportChat({\n adapter,\n channel,\n institutionId,\n user,\n });\n const [savedContactRequests, setSavedContactRequests] = useState<string[]>(\n []\n );\n\n // Latest AI request for name/e-mail: the form goes under it, and the\n // composer waits until it is sent\n const contactRequest = updateContact\n ? [...messages].reverse().find((message) => message.contactRequest?.length)\n : undefined;\n const awaitingContact =\n !!contactRequest && !savedContactRequests.includes(contactRequest.id);\n\n const isOpen = windowState === 'open';\n\n const canOpenArticle = !!adapter.getArticle && !!institutionId;\n const loadArticle = useCallback(\n (articleId: string) => {\n if (!adapter.getArticle || !institutionId) {\n return Promise.reject(new Error('Artigo indisponível'));\n }\n // Called on the adapter so a class-based adapter keeps its `this`\n return adapter.getArticle(institutionId, articleId);\n },\n [adapter, institutionId]\n );\n const openArticle = canOpenArticle\n ? (target: { id: string; title?: string }) => {\n setArticle(target);\n setExpanded(true);\n }\n : undefined;\n const closeArticle = () => {\n setArticle(null);\n setExpanded(false);\n };\n const title = theme?.title ?? 'Suporte';\n const left = theme?.position === 'bottom-left';\n const accentStyle = theme?.primaryColor\n ? { backgroundColor: theme.primaryColor }\n : undefined;\n\n // Count the support replies that arrive while the window is not visible\n useEffect(() => {\n const fresh = messages.slice(seenCount.current);\n seenCount.current = messages.length;\n if (isOpen) return;\n const replies = fresh.filter((message) => !isOwnMessage(message)).length;\n if (replies) setUnread((count) => count + replies);\n }, [messages, isOpen]);\n\n useEffect(() => {\n if (!isOpen || article) return;\n setUnread(0);\n listRef.current?.scrollTo?.({ top: listRef.current.scrollHeight });\n }, [isOpen, messages, article]);\n\n const open = () => {\n setWindowState('open');\n onOpen?.();\n };\n\n const close = () => {\n setWindowState('closed');\n onClose?.();\n };\n\n /** Drop a finished conversation so the next message opens a new ticket */\n const startOver = () => {\n setDraft('');\n setFiles([]);\n setFileError(null);\n setUnread(0);\n seenCount.current = 0;\n setSavedContactRequests([]);\n reset();\n };\n\n const submit = async () => {\n const content = draft;\n const pending = files;\n if (!content.trim() && pending.length === 0) return;\n setDraft('');\n setFiles([]);\n setFileError(null);\n const message = await send(content, pending);\n if (message) {\n onSend?.(message);\n } else {\n setDraft(content);\n setFiles(pending);\n }\n };\n\n const handleFiles = (event: ChangeEvent<HTMLInputElement>) => {\n const { accepted, problem } = pickAttachments(\n files,\n Array.from(event.target.files ?? [])\n );\n setFiles(accepted);\n setFileError(problem);\n event.target.value = '';\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n void submit();\n }\n };\n\n return (\n <div\n className={cn(\n 'fixed bottom-4 z-50 flex flex-col gap-3',\n left ? 'left-4 items-start' : 'right-4 items-end'\n )}\n >\n {isOpen && (\n <section\n role=\"dialog\"\n aria-label={title}\n className={cn(\n 'max-w-[calc(100vw-2rem)] max-h-[calc(100vh-6rem)] flex flex-col bg-background rounded-2xl shadow-lg border border-border-100 overflow-hidden transition-[width,height]',\n expanded ? 'w-[720px] h-[615px]' : 'w-[360px] h-[520px]'\n )}\n >\n {article ? (\n <SupportChatArticleView\n articleId={article.id}\n initialTitle={article.title}\n loadArticle={loadArticle}\n expanded={expanded}\n onToggleExpanded={() => setExpanded((value) => !value)}\n onBack={closeArticle}\n externalUrl={faqArticleUrl?.(article.id)}\n accentStyle={accentStyle}\n />\n ) : (\n <>\n <header\n className=\"flex items-center justify-between px-4 py-3 bg-primary-500 text-white\"\n style={accentStyle}\n >\n <Text size=\"md\" weight=\"semibold\" color=\"\">\n {title}\n </Text>\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"raw\"\n aria-label=\"Minimizar\"\n onClick={() => setWindowState('minimized')}\n className=\"p-1 rounded hover:bg-white/20\"\n >\n <MinusIcon size={18} />\n </Button>\n <Button\n variant=\"raw\"\n aria-label=\"Fechar\"\n onClick={close}\n className=\"p-1 rounded hover:bg-white/20\"\n >\n <XIcon size={18} />\n </Button>\n </div>\n </header>\n\n <div ref={listRef} className=\"flex-1 overflow-y-auto px-4 py-3\">\n {messages.length === 0 ? (\n <Text size=\"sm\" className=\"text-text-500 text-center mt-8\">\n Olá! Como podemos te ajudar?\n </Text>\n ) : (\n messages.map((message) => (\n <div key={message.id}>\n <MessageBubble\n message={message}\n faqArticleUrl={faqArticleUrl}\n onOpenArticle={openArticle}\n />\n {updateContact &&\n message.id === contactRequest?.id &&\n message.contactRequest && (\n <SupportChatContactForm\n fields={message.contactRequest}\n accentColor={theme?.primaryColor}\n onSubmit={async (contact) => {\n const ok = await updateContact(contact);\n if (ok) {\n setSavedContactRequests((ids) => [\n ...ids,\n message.id,\n ]);\n }\n return ok;\n }}\n />\n )}\n </div>\n ))\n )}\n </div>\n\n {(fileError || error) && (\n <Text\n size=\"xs\"\n role=\"alert\"\n className=\"px-4 py-2 text-error-700 bg-error-50\"\n >\n {fileError ?? error}\n </Text>\n )}\n\n {closed ? (\n <div className=\"flex flex-col items-center gap-2 px-4 py-3 border-t border-border-100\">\n <Text size=\"sm\" color=\"text-text-500\" className=\"text-center\">\n Este atendimento foi encerrado.\n </Text>\n <Button variant=\"link\" size=\"small\" onClick={startOver}>\n Iniciar nova conversa\n </Button>\n </div>\n ) : (\n <form\n className=\"flex flex-col gap-2 px-3 py-3 border-t border-border-100\"\n onSubmit={(event) => {\n event.preventDefault();\n void submit();\n }}\n >\n {files.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {files.map((file, index) => (\n <PendingAttachment\n key={`${file.name}-${index}`}\n file={file}\n onRemove={() =>\n setFiles((list) =>\n list.filter((_, i) => i !== index)\n )\n }\n />\n ))}\n </div>\n )}\n <div className=\"flex items-end gap-2\">\n <input\n ref={fileInputRef}\n type=\"file\"\n accept={SUPPORT_CHAT_ACCEPT}\n multiple\n hidden\n data-testid=\"support-chat-file-input\"\n onChange={handleFiles}\n />\n <Button\n variant=\"raw\"\n aria-label=\"Anexar arquivo\"\n disabled={\n sending ||\n awaitingContact ||\n files.length >= SUPPORT_CHAT_MAX_ATTACHMENTS\n }\n onClick={() => fileInputRef.current?.click()}\n className=\"p-2 rounded-full text-text-600 hover:bg-background-100 disabled:opacity-50\"\n >\n <PaperclipIcon size={20} />\n </Button>\n <textarea\n aria-label=\"Mensagem\"\n placeholder={\n awaitingContact\n ? 'Preencha seus dados acima'\n : 'Digite sua mensagem'\n }\n disabled={awaitingContact}\n rows={1}\n value={draft}\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={handleKeyDown}\n className=\"flex-1 resize-none rounded-xl border border-border-300 px-3 py-2 text-sm text-text-900 focus:outline-none focus:border-primary-500 max-h-28\"\n />\n <Button\n variant=\"raw\"\n type=\"submit\"\n aria-label=\"Enviar\"\n disabled={\n sending ||\n awaitingContact ||\n (!draft.trim() && files.length === 0)\n }\n className=\"p-2 rounded-full bg-primary-500 text-white disabled:opacity-50\"\n style={accentStyle}\n >\n <PaperPlaneTiltIcon size={18} />\n </Button>\n </div>\n </form>\n )}\n </>\n )}\n </section>\n )}\n\n {!isOpen && (\n <Button\n variant=\"raw\"\n aria-label={`Abrir ${title.toLowerCase()}`}\n onClick={open}\n className=\"relative w-14 h-14 rounded-full bg-primary-500 text-white shadow-lg flex items-center justify-center hover:brightness-110\"\n style={accentStyle}\n >\n <ChatCircleDotsIcon size={28} />\n {unread > 0 && (\n <Text\n as=\"span\"\n size=\"xs\"\n color=\"text-white\"\n aria-label={formatNewMessagesLabel(unread)}\n className=\"absolute -top-1 -right-1 min-w-5 h-5 px-1 rounded-full bg-error-500 flex items-center justify-center\"\n >\n {unread}\n </Text>\n )}\n </Button>\n )}\n </div>\n );\n};\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n SUPPORT_CHAT_AUTHOR,\n SUPPORT_CHAT_GUEST_SENDER_ID,\n SUPPORT_CHAT_STATUS,\n type SupportChatAdapter,\n type SupportChatContact,\n type SupportChatEvent,\n type SupportChatMessage,\n type SupportChatTicket,\n type SupportChatUser,\n} from '../types/supportChat';\n\nexport interface UseSupportChatConfig {\n adapter: SupportChatAdapter;\n channel: string;\n institutionId?: string;\n user?: SupportChatUser;\n}\n\nexport interface UseSupportChatReturn {\n ticket: SupportChatTicket | null;\n messages: SupportChatMessage[];\n sending: boolean;\n error: string | null;\n /** Ticket resolved: no more messages can be sent */\n closed: boolean;\n /** Send a message (text and/or files); the first one opens the ticket */\n send: (content: string, files?: File[]) => Promise<SupportChatMessage | null>;\n /**\n * Save the contact typed in the form shown under the AI request. Resolves\n * false (and sets `error`) when it fails. Undefined when the adapter cannot\n * save it.\n */\n updateContact?: (contact: SupportChatContact) => Promise<boolean>;\n /** Forget the current conversation */\n reset: () => void;\n}\n\nlet pendingSequence = 0;\n\n/**\n * Message shown right away while it is being sent (optimistic rendering).\n * Images use local object URLs, released when the real message arrives.\n */\nconst buildPendingMessage = (\n content: string,\n files: File[],\n user?: SupportChatUser\n): SupportChatMessage => {\n pendingSequence += 1;\n return {\n id: `pending-${pendingSequence}`,\n senderId: SUPPORT_CHAT_GUEST_SENDER_ID,\n senderName: user?.name || 'Você',\n senderPhoto: null,\n senderRole: user?.email\n ? SUPPORT_CHAT_AUTHOR.USER\n : SUPPORT_CHAT_AUTHOR.GUEST,\n content,\n messageType: 'text',\n createdAt: new Date().toISOString(),\n attachments: files.map((file, index) => ({\n id: `pending-${pendingSequence}-${index}`,\n url: URL.createObjectURL(file),\n fileName: file.name,\n mimeType: file.type,\n size: file.size,\n })),\n pending: true,\n };\n};\n\nconst releasePending = (message: SupportChatMessage) =>\n message.attachments.forEach((item) => URL.revokeObjectURL(item.url));\n\nconst getErrorMessage = (error: unknown) =>\n error instanceof Error ? error.message : 'Não foi possível enviar a mensagem';\n\n/** Add or replace a message, keeping the list ordered by arrival */\nconst upsertMessage = (\n list: SupportChatMessage[],\n message: SupportChatMessage\n): SupportChatMessage[] =>\n list.some((item) => item.id === message.id)\n ? list.map((item) => (item.id === message.id ? message : item))\n : [...list, message];\n\n/**\n * State of a support conversation on top of a SupportChatAdapter.\n * The REST-like calls write; the subscription only applies the events,\n * so a message received both ways is stored once.\n */\nexport const useSupportChat = ({\n adapter,\n channel,\n institutionId,\n user,\n}: UseSupportChatConfig): UseSupportChatReturn => {\n const [ticket, setTicket] = useState<SupportChatTicket | null>(null);\n const [messages, setMessages] = useState<SupportChatMessage[]>([]);\n const [sending, setSending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const sendingRef = useRef(false);\n\n const ticketId = ticket?.id;\n\n useEffect(() => {\n if (!ticketId) return;\n\n const handleEvent = (event: SupportChatEvent) => {\n switch (event.type) {\n case 'new_message':\n setMessages((list) => upsertMessage(list, event.payload.message));\n break;\n case 'message_updated':\n setMessages((list) =>\n list.map((item) =>\n item.id === event.payload.messageId\n ? { ...item, content: event.payload.content }\n : item\n )\n );\n break;\n case 'ticket_updated': {\n const { ticketId: id, ...rest } = event.payload;\n setTicket((current) =>\n current?.id === id ? { ...current, ...rest } : current\n );\n break;\n }\n case 'error':\n setError(event.payload.message);\n break;\n }\n };\n\n return adapter.subscribe(ticketId, handleEvent);\n }, [adapter, ticketId]);\n\n const send = useCallback(\n async (content: string, files: File[] = []) => {\n const text = content.trim();\n if ((!text && files.length === 0) || sendingRef.current) return null;\n\n sendingRef.current = true;\n setSending(true);\n setError(null);\n\n const pending = buildPendingMessage(text, files, user);\n setMessages((list) => [...list, pending]);\n // Swap the optimistic message for the confirmed ones\n const confirm = (confirmed: SupportChatMessage[]) =>\n setMessages((list) =>\n confirmed.reduce(\n upsertMessage,\n list.filter((item) => item.id !== pending.id)\n )\n );\n\n try {\n if (ticketId) {\n const message = await adapter.sendMessage(ticketId, text, files);\n confirm([message]);\n return message;\n }\n\n const started = await adapter.startConversation({\n message: text,\n channel,\n institutionId,\n requesterName: user?.name,\n email: user?.email,\n files,\n });\n setTicket(started.ticket);\n confirm(started.messages);\n return started.messages[started.messages.length - 1] ?? null;\n } catch (sendError) {\n setMessages((list) => list.filter((item) => item.id !== pending.id));\n setError(getErrorMessage(sendError));\n return null;\n } finally {\n releasePending(pending);\n sendingRef.current = false;\n setSending(false);\n }\n },\n [adapter, channel, institutionId, ticketId, user]\n );\n\n const updateContact = useCallback(\n async (contact: SupportChatContact) => {\n if (!ticketId || !adapter.updateContact) return false;\n setError(null);\n try {\n await adapter.updateContact(ticketId, contact);\n return true;\n } catch (contactError) {\n setError(\n (contactError as { response?: { status?: number } } | null)?.response\n ?.status === 409\n ? 'O e-mail deste atendimento já foi informado'\n : 'Não foi possível salvar seus dados. Tente de novo.'\n );\n return false;\n }\n },\n [adapter, ticketId]\n );\n\n const reset = useCallback(() => {\n setTicket(null);\n setMessages([]);\n setError(null);\n }, []);\n\n return {\n ticket,\n messages,\n sending,\n error,\n closed: ticket?.status === SUPPORT_CHAT_STATUS.FECHADO,\n send,\n updateContact: adapter.updateContact ? updateContact : undefined,\n reset,\n };\n};\n","import { useState, type FormEvent } from 'react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport Input from '../Input/Input';\nimport type {\n SupportChatContact,\n SupportChatContactField,\n} from '../../types/supportChat';\n\nexport interface SupportChatContactFormProps {\n /** Fields the AI asked for, in order (one step each) */\n fields: SupportChatContactField[];\n /** Saves the contact; resolves true when it worked */\n onSubmit: (contact: SupportChatContact) => Promise<boolean>;\n /** Accent color of the theme, for the button */\n accentColor?: string;\n}\n\nconst STEPS: Record<\n SupportChatContactField,\n { label: string; type: string; autoComplete: string; placeholder: string }\n> = {\n name: {\n label: 'Seu nome',\n type: 'text',\n autoComplete: 'name',\n placeholder: 'Como podemos te chamar?',\n },\n email: {\n label: 'Seu e-mail',\n type: 'email',\n autoComplete: 'email',\n placeholder: 'nome@exemplo.com',\n },\n};\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/** Problem with a typed value, or null when it can be sent */\nconst validate = (\n field: SupportChatContactField,\n value: string\n): string | null => {\n if (field === 'name') {\n return value.length >= 2 ? null : 'Digite seu nome';\n }\n return EMAIL_PATTERN.test(value) ? null : 'Digite um e-mail válido';\n};\n\n/**\n * Step-by-step form for the contact the AI asks on the first contact (name,\n * then e-mail), shown under the AI message like the Zendesk pre-chat form.\n * The values go to the backend as fields, not as a chat message. Once sent,\n * it shows what was saved.\n */\nexport const SupportChatContactForm = ({\n fields,\n onSubmit,\n accentColor,\n}: SupportChatContactFormProps) => {\n const [step, setStep] = useState(0);\n const [values, setValues] = useState<Record<string, string>>({});\n const [problem, setProblem] = useState<string | null>(null);\n const [saving, setSaving] = useState(false);\n const [saved, setSaved] = useState(false);\n\n const field = fields[step];\n const config = STEPS[field];\n const value = values[field] ?? '';\n const isLast = step === fields.length - 1;\n\n if (saved) {\n return (\n <div\n data-testid=\"support-chat-contact-saved\"\n className=\"mb-3 ml-1 flex max-w-[85%] flex-col gap-1 rounded-2xl border border-border-100 px-4 py-3\"\n >\n {fields.map((item) => (\n <Text key={item} size=\"xs\" color=\"text-text-600\">\n {STEPS[item].label}:{' '}\n <Text as=\"span\" size=\"xs\" weight=\"semibold\" color=\"text-text-900\">\n {values[item]}\n </Text>\n </Text>\n ))}\n </div>\n );\n }\n\n const advance = async (event: FormEvent) => {\n event.preventDefault();\n const typed = field === 'email' ? value.trim().toLowerCase() : value.trim();\n const error = validate(field, typed);\n setProblem(error);\n if (error) return;\n\n const next = { ...values, [field]: typed };\n setValues(next);\n if (!isLast) {\n setStep(step + 1);\n return;\n }\n\n setSaving(true);\n const ok = await onSubmit({\n ...(next.name ? { requesterName: next.name } : {}),\n ...(next.email ? { email: next.email } : {}),\n });\n setSaving(false);\n if (ok) setSaved(true);\n };\n\n const inputId = `support-chat-contact-${field}`;\n\n return (\n <form\n aria-label=\"Seus dados\"\n noValidate\n onSubmit={(event) => void advance(event)}\n className=\"mb-3 ml-1 flex max-w-[85%] flex-col gap-3 rounded-2xl border border-border-100 px-4 py-3\"\n >\n <Input\n id={inputId}\n key={field}\n label={config.label}\n variant=\"rounded\"\n size=\"small\"\n type={config.type}\n autoComplete={config.autoComplete}\n placeholder={config.placeholder}\n value={value}\n autoFocus\n disabled={saving}\n errorMessage={problem ?? undefined}\n onChange={(event) =>\n setValues((current) => ({ ...current, [field]: event.target.value }))\n }\n />\n <div className=\"flex items-center justify-between\">\n <Text size=\"xs\" color=\"text-text-500\">\n {fields.length > 1 ? `${step + 1} de ${fields.length}` : ''}\n </Text>\n <Button\n type=\"submit\"\n size=\"small\"\n disabled={saving}\n style={accentColor ? { backgroundColor: accentColor } : undefined}\n >\n {isLast ? 'Enviar' : 'Avançar'}\n </Button>\n </div>\n </form>\n );\n};\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport {\n ArrowLeftIcon,\n ArrowSquareOutIcon,\n ArrowsInSimpleIcon,\n ArrowsOutSimpleIcon,\n} from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport { ArticleContent } from '../ArticleContent/ArticleContent';\nimport { embedVideoLinks } from '../../utils/videoEmbed';\nimport type { SupportChatArticle } from '../../types/supportChat';\nimport { cn } from '../../utils/utils';\n\nexport interface SupportChatArticleViewProps {\n /** Article to show */\n articleId: string;\n /** Title known from the chat, shown while the article loads */\n initialTitle?: string;\n /** Loads the article (adapter.getArticle bound to the institution) */\n loadArticle: (articleId: string) => Promise<SupportChatArticle>;\n /** Whether the chat window is in the wide mode */\n expanded: boolean;\n onToggleExpanded: () => void;\n /** Back to the conversation (it is kept as it was) */\n onBack: () => void;\n /** Page of the article in the help center, for \"Abrir em nova aba\" */\n externalUrl?: string;\n /** Header background (theme) */\n accentStyle?: { backgroundColor: string };\n}\n\nconst formatUpdatedAt = (iso: string) =>\n new Date(iso).toLocaleDateString('pt-BR', {\n day: '2-digit',\n month: '2-digit',\n year: 'numeric',\n });\n\n/**\n * FAQ article read inside the support chat, like the Zendesk messenger: the\n * conversation is replaced by the article (no new tab), with back,\n * expand/reduce and open-in-new-tab controls. The big title moves to the\n * header once it scrolls out of view. YouTube/Vimeo links of the article\n * become players.\n */\nexport const SupportChatArticleView = ({\n articleId,\n initialTitle,\n loadArticle,\n expanded,\n onToggleExpanded,\n onBack,\n externalUrl,\n accentStyle,\n}: SupportChatArticleViewProps) => {\n const [article, setArticle] = useState<SupportChatArticle | null>(null);\n const [failed, setFailed] = useState(false);\n const [titleHidden, setTitleHidden] = useState(false);\n const titleRef = useRef<HTMLHeadingElement>(null);\n\n useEffect(() => {\n let active = true;\n setArticle(null);\n setFailed(false);\n loadArticle(articleId)\n .then((loaded) => {\n if (active) setArticle(loaded);\n })\n .catch(() => {\n if (active) setFailed(true);\n });\n return () => {\n active = false;\n };\n }, [articleId, loadArticle]);\n\n // The big title leaves the screen → show it in the header\n useEffect(() => {\n const title = titleRef.current;\n if (!title || typeof IntersectionObserver === 'undefined') {\n return undefined;\n }\n const observer = new IntersectionObserver(([entry]) =>\n setTitleHidden(!entry.isIntersecting)\n );\n observer.observe(title);\n return () => observer.disconnect();\n }, [article]);\n\n const html = useMemo(\n () => (article ? embedVideoLinks(article.body) : ''),\n [article]\n );\n const title = article?.title ?? initialTitle ?? 'Artigo';\n\n return (\n <div\n data-testid=\"support-chat-article\"\n className=\"flex min-h-0 flex-1 flex-col\"\n >\n <header\n className=\"flex items-center gap-1 px-2 py-2 bg-primary-500 text-white\"\n style={accentStyle}\n >\n <Button\n variant=\"raw\"\n aria-label=\"Voltar para a janela de mensagens\"\n onClick={onBack}\n className=\"p-1 rounded hover:bg-white/20\"\n >\n <ArrowLeftIcon size={18} />\n </Button>\n <Text\n as=\"span\"\n size=\"sm\"\n weight=\"semibold\"\n color=\"\"\n aria-hidden={!titleHidden || undefined}\n className={cn(\n 'min-w-0 flex-1 truncate transition-opacity',\n titleHidden ? 'opacity-100' : 'opacity-0'\n )}\n >\n {title}\n </Text>\n <Button\n variant=\"raw\"\n aria-label={expanded ? 'Reduzir artigo' : 'Expandir artigo'}\n onClick={onToggleExpanded}\n className=\"p-1 rounded hover:bg-white/20\"\n >\n {expanded ? (\n <ArrowsInSimpleIcon size={18} />\n ) : (\n <ArrowsOutSimpleIcon size={18} />\n )}\n </Button>\n {externalUrl && (\n <a\n href={externalUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n aria-label=\"Abrir em nova aba\"\n className=\"p-1 rounded hover:bg-white/20\"\n >\n <ArrowSquareOutIcon size={18} />\n </a>\n )}\n </header>\n\n <div className=\"flex-1 overflow-y-auto px-5 py-4\">\n <Text\n as=\"h2\"\n ref={titleRef}\n size=\"xl\"\n weight=\"bold\"\n color=\"text-text-950\"\n >\n {title}\n </Text>\n {article?.updatedAt && (\n <Text size=\"xs\" color=\"text-text-500\" className=\"mt-1\">\n Atualizado em {formatUpdatedAt(article.updatedAt)}\n </Text>\n )}\n\n <div className=\"mt-4\">\n {failed && (\n <Text size=\"sm\" role=\"alert\" color=\"text-error-700\">\n Não foi possível abrir o artigo.\n </Text>\n )}\n {!failed && !article && (\n <Text size=\"sm\" color=\"text-text-500\">\n Carregando…\n </Text>\n )}\n {article && <ArticleContent html={html} />}\n </div>\n </div>\n </div>\n );\n};\n","import { useEffect, useState, type ReactNode } from 'react';\nimport {\n FileIcon,\n FilePdfIcon,\n FileVideoIcon,\n XIcon,\n} from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport {\n SUPPORT_CHAT_ATTACHMENT_RULES,\n SUPPORT_CHAT_MAX_ATTACHMENTS,\n type SupportChatAttachment,\n} from '../../types/supportChat';\n\n/** Value of the `accept` attribute of the file picker */\nexport const SUPPORT_CHAT_ACCEPT = Object.keys(\n SUPPORT_CHAT_ATTACHMENT_RULES\n).join(',');\n\nconst isImage = (mimeType?: string) => Boolean(mimeType?.startsWith('image/'));\nconst isVideo = (mimeType?: string) => Boolean(mimeType?.startsWith('video/'));\n\nconst formatSize = (bytes: number) => `${Math.round(bytes / (1024 * 1024))} MB`;\n\n/**\n * Keep the files the backend accepts (type, size and count) and say why the\n * others were left out.\n * @param current - Files already picked\n * @param picked - Files just chosen\n * @returns The accepted files and the last problem found, if any\n */\nexport const pickAttachments = (current: File[], picked: File[]) => {\n const accepted = [...current];\n let problem: string | null = null;\n for (const file of picked) {\n const maxBytes = SUPPORT_CHAT_ATTACHMENT_RULES[file.type];\n if (!maxBytes) {\n problem =\n 'Envie imagens (JPG, PNG, GIF, WEBP), PDF ou vídeo (MP4, WEBM, MOV)';\n } else if (file.size > maxBytes) {\n problem = `${file.name} passa de ${formatSize(maxBytes)}`;\n } else if (accepted.length >= SUPPORT_CHAT_MAX_ATTACHMENTS) {\n problem = `Máximo de ${SUPPORT_CHAT_MAX_ATTACHMENTS} arquivos por mensagem`;\n } else {\n accepted.push(file);\n }\n }\n return { accepted, problem };\n};\n\nconst FileTypeIcon = ({ mimeType }: { mimeType?: string }) => {\n if (mimeType === 'application/pdf') return <FilePdfIcon size={18} />;\n if (isVideo(mimeType)) return <FileVideoIcon size={18} />;\n return <FileIcon size={18} />;\n};\n\n/** Files of a message: images and videos inline, other files as links */\nexport const AttachmentList = ({\n items,\n}: {\n items: SupportChatAttachment[];\n}) => (\n <div className=\"flex flex-wrap gap-2 first:mt-0 mt-1\">\n {items.map((item) => {\n if (isImage(item.mimeType)) {\n return (\n <a key={item.id} href={item.url} target=\"_blank\" rel=\"noreferrer\">\n <img\n src={item.url}\n alt={item.fileName}\n className=\"max-w-[200px] max-h-[200px] rounded-lg object-cover\"\n />\n </a>\n );\n }\n if (isVideo(item.mimeType)) {\n return (\n <video\n key={item.id}\n src={item.url}\n controls\n preload=\"metadata\"\n aria-label={item.fileName}\n className=\"max-w-[240px] max-h-[200px] rounded-lg bg-black\"\n />\n );\n }\n return (\n <a\n key={item.id}\n href={item.url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"flex items-center gap-1 underline\"\n >\n <FileTypeIcon mimeType={item.mimeType} />\n <Text as=\"span\" size=\"sm\" color=\"\">\n {item.fileName}\n </Text>\n </a>\n );\n })}\n </div>\n);\n\n/** A file picked but not sent yet: thumbnail for images, name for the rest */\nexport const PendingAttachment = ({\n file,\n onRemove,\n}: {\n file: File;\n onRemove: () => void;\n}) => {\n const image = isImage(file.type);\n const [url, setUrl] = useState('');\n useEffect(() => {\n if (!image) return undefined;\n const objectUrl = URL.createObjectURL(file);\n setUrl(objectUrl);\n return () => URL.revokeObjectURL(objectUrl);\n }, [file, image]);\n\n return (\n <div className=\"relative\">\n {image ? (\n url && (\n <img\n src={url}\n alt={file.name}\n className=\"w-14 h-14 rounded-lg object-cover border border-border-100\"\n />\n )\n ) : (\n <div className=\"flex h-14 max-w-[160px] items-center gap-1 rounded-lg border border-border-100 px-2 text-text-700\">\n <FileTypeIcon mimeType={file.type} />\n <Text as=\"span\" size=\"xs\" className=\"truncate\">\n {file.name}\n </Text>\n </div>\n )}\n <Button\n variant=\"raw\"\n aria-label={`Remover ${file.name}`}\n onClick={onRemove}\n className=\"absolute -top-1 -right-1 w-5 h-5 rounded-full bg-text-700 text-white flex items-center justify-center\"\n >\n <XIcon size={12} />\n </Button>\n </div>\n );\n};\n\n/** http(s) addresses inside a message */\nconst URL_PATTERN = /https?:\\/\\/[^\\s<>\"]+/g;\n\n/** Punctuation that ends a sentence, not the address */\nconst TRAILING_PUNCTUATION = /[.,;:!?)\\]]+$/;\n\n/** A FAQ article page: `/artigo/<uuid>` */\nconst ARTICLE_PATH =\n /\\/artigo\\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i;\n\n/**\n * Message text with its addresses as links. A link to a FAQ article opens in\n * the chat (when `onOpenArticle` is given); the others open a new tab.\n */\nexport const MessageText = ({\n text,\n onOpenArticle,\n}: {\n text: string;\n onOpenArticle?: (articleId: string) => void;\n}) => {\n const parts: ReactNode[] = [];\n let cursor = 0;\n for (const match of text.matchAll(URL_PATTERN)) {\n const raw = match[0];\n const url = raw.replace(TRAILING_PUNCTUATION, '');\n const start = match.index ?? 0;\n if (start > cursor) parts.push(text.slice(cursor, start));\n\n const articleId = onOpenArticle ? ARTICLE_PATH.exec(url)?.[1] : undefined;\n parts.push(\n <a\n key={`${start}-${url}`}\n href={url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"underline break-all\"\n onClick={\n articleId\n ? (event) => {\n event.preventDefault();\n onOpenArticle?.(articleId);\n }\n : undefined\n }\n >\n {url}\n </a>\n );\n cursor = start + url.length;\n }\n if (cursor < text.length) parts.push(text.slice(cursor));\n\n return (\n <Text size=\"sm\" color=\"\" className=\"whitespace-pre-wrap\">\n {parts}\n </Text>\n );\n};\n","import { useMemo } from 'react';\nimport { SupportChatWidget } from './SupportChatWidget';\nimport { createGuestSupportChatAdapter } from './guestSupportChatAdapter';\nimport { useSupportFeatureFlag } from '../../hooks/useSupportFeatureFlag';\nimport { useAppStore } from '../../store/appStore';\nimport { useAuthStore } from '../../store/authStore';\nimport { SupportType, type SupportApiClient } from '../../types/support';\nimport type {\n SupportChatHttpClient,\n SupportChatTheme,\n} from '../../types/supportChat';\n\nexport interface NativeSupportChatProps {\n /** Client of the backend API (the app axios instance fits as it is) */\n apiClient: SupportApiClient & SupportChatHttpClient;\n /** App the chat runs in, sent with the ticket (e.g. \"aluno\", \"login\") */\n channel: string;\n /**\n * Extra condition of the app to show the chat, on top of the SUPPORT flag\n * (e.g. logged in and with the support module). Default: true.\n */\n enabled?: boolean;\n /**\n * Link of a FAQ article. Default: the FAQ address of the SUPPORT flag\n * (`<faqUrl>/artigo/<id>`).\n */\n faqArticleUrl?: (articleId: string) => string;\n /** Support WebSocket; without it the chat polls the REST API */\n wsUrl?: string;\n theme?: SupportChatTheme;\n}\n\n/**\n * Native support chat ready to drop in an app: reads the SUPPORT feature flag\n * of the institution (from the app store), the logged user (from the auth\n * store) and talks to the support API by itself.\n *\n * Shown only when the institution uses the native support with the internal\n * support and its chat switched on in the backoffice, once the flag is read,\n * and when `enabled`. With Zendesk it renders nothing (the app loads the\n * Zendesk widget). A logged user opens the ticket with their name and e-mail;\n * a visitor gives them to the AI on the first contact.\n *\n * @example\n * <NativeSupportChat apiClient={api} channel=\"aluno\" enabled={hasSupport} />\n */\nexport const NativeSupportChat = ({\n apiClient,\n channel,\n enabled = true,\n faqArticleUrl,\n wsUrl,\n theme,\n}: NativeSupportChatProps) => {\n const { institutionId } = useAppStore();\n const userName = useAuthStore(\n (state) => state.user?.name ?? state.sessionInfo?.userName\n );\n const userEmail = useAuthStore((state) => state.user?.email);\n const flag = useSupportFeatureFlag({ apiClient });\n const adapter = useMemo(\n () => createGuestSupportChatAdapter({ apiClient, wsUrl }),\n [apiClient, wsUrl]\n );\n\n const user =\n userName || userEmail ? { name: userName, email: userEmail } : undefined;\n\n return (\n <SupportChatWidget\n adapter={adapter}\n channel={channel}\n institutionId={institutionId || undefined}\n user={user}\n theme={theme}\n faqArticleUrl={faqArticleUrl ?? flag.faqArticleUrl}\n flags={{\n enabled: enabled && !flag.loading && flag.isNative && flag.chatEnabled,\n supportType: flag.isNative ? SupportType.NATIVE : SupportType.ZENDESK,\n }}\n />\n );\n};\n","import { isSupportTicketOrigin } from '../../types/support';\nimport {\n SUPPORT_CHAT_AUTHOR,\n SUPPORT_CHAT_GUEST_SENDER_ID,\n SUPPORT_CHAT_STATUS,\n type SupportChatAdapter,\n type SupportChatAnswerApi,\n type SupportChatArticle,\n type SupportChatEvent,\n type SupportChatHttpClient,\n type SupportChatMessage,\n} from '../../types/supportChat';\n\nexport interface GuestSupportChatAdapterOptions {\n /** Client of the backend API (e.g. the app axios instance) */\n apiClient: SupportChatHttpClient;\n /**\n * Support WebSocket, including ?channel=support (e.g.\n * wss://bff-hml-chat.analyticaensino.com.br?channel=support).\n * Without it the adapter only polls the REST API.\n */\n wsUrl?: string;\n /** Polling interval while the socket is not connected, in ms */\n pollIntervalMs?: number;\n}\n\nconst GUEST_TOKEN_HEADER = 'x-support-guest-token';\n\nconst senderNameFor = (role: string, requesterName: string) => {\n if (role === SUPPORT_CHAT_AUTHOR.AGENT) return 'Suporte';\n if (role === SUPPORT_CHAT_AUTHOR.AI) return 'Assistente';\n return requesterName;\n};\n\n/** REST answer → widget message */\nexport const fromSupportAnswer = (\n answer: SupportChatAnswerApi,\n requesterName: string\n): SupportChatMessage => ({\n id: answer.id,\n senderId:\n answer.authorType === SUPPORT_CHAT_AUTHOR.GUEST\n ? SUPPORT_CHAT_GUEST_SENDER_ID\n : answer.authorType.toLowerCase(),\n senderName: senderNameFor(answer.authorType, requesterName),\n senderPhoto: null,\n senderRole: answer.authorType,\n content: answer.answer,\n messageType: 'text',\n createdAt: answer.createdAt,\n attachments: answer.attachments ?? [],\n faq:\n answer.faqArticleId && answer.faqArticleTitle\n ? { id: answer.faqArticleId, title: answer.faqArticleTitle }\n : null,\n contactRequest: answer.contactRequest ?? null,\n});\n\n/**\n * Support chat adapter for visitors (no login), on top of the support API:\n * - POST /support/external opens the ticket and returns the guest token;\n * - GET/POST /support/guest/:id/answer read and send, with the token header;\n * - the chat WebSocket (?channel=support&roomId=&token=) pushes the events,\n * and REST polling covers the time the socket is down.\n */\nexport const createGuestSupportChatAdapter = ({\n apiClient,\n wsUrl,\n pollIntervalMs = 3000,\n}: GuestSupportChatAdapterOptions): SupportChatAdapter => {\n const sessions = new Map<string, { token: string; name: string }>();\n /**\n * Ticket opening text written by the adapter (an image-only start still\n * needs a description). It is the ticket description for the agents, not\n * something the visitor typed, so the chat does not show it.\n */\n const hiddenIds = new Set<string>();\n const isVisible = (message: SupportChatMessage) => !hiddenIds.has(message.id);\n\n const headersFor = (ticketId: string) => ({\n [GUEST_TOKEN_HEADER]: sessions.get(ticketId)?.token ?? '',\n });\n const nameFor = (ticketId: string) =>\n sessions.get(ticketId)?.name ?? 'Visitante';\n\n const listMessages = async (ticketId: string) => {\n const { data } = await apiClient.get<{ data: SupportChatAnswerApi[] }>(\n `/support/guest/${ticketId}/answer`,\n { headers: headersFor(ticketId) }\n );\n return data.data\n .map((answer) => fromSupportAnswer(answer, nameFor(ticketId)))\n .filter(isVisible);\n };\n\n /**\n * Upload each file straight to the storage: ask the backend for a signed\n * URL, then PUT the file there. Returns what the message needs.\n */\n const uploadFiles = (ticketId: string, files: File[]) =>\n Promise.all(\n files.map(async (file) => {\n const { data } = await apiClient.post<{\n data: { uploadUrl: string; key: string; contentType: string };\n }>(\n `/support/guest/${ticketId}/upload-url`,\n { fileName: file.name, fileSize: file.size, mimeType: file.type },\n { headers: headersFor(ticketId) }\n );\n const { uploadUrl, key, contentType } = data.data;\n const response = await fetch(uploadUrl, {\n method: 'PUT',\n headers: { 'Content-Type': contentType },\n body: file,\n });\n if (!response.ok) {\n throw new Error(`Não foi possível enviar ${file.name}`);\n }\n return {\n key,\n fileName: file.name,\n mimeType: contentType,\n size: file.size,\n };\n })\n );\n\n const sendMessage = async (\n ticketId: string,\n content: string,\n files: File[] = []\n ) => {\n const attachments = await uploadFiles(ticketId, files);\n const { data } = await apiClient.post<{ data: SupportChatAnswerApi }>(\n `/support/guest/${ticketId}/answer`,\n { answer: content, attachments },\n { headers: headersFor(ticketId) }\n );\n return fromSupportAnswer(data.data, nameFor(ticketId));\n };\n\n return {\n async startConversation({\n message,\n channel,\n institutionId,\n requesterName,\n email,\n files = [],\n }) {\n // The ticket needs a description; an image-only start uses the names\n const description = message || files.map((file) => file.name).join(', ');\n const { data } = await apiClient.post<{\n data: { id: string; guestToken: string };\n }>('/support/external', {\n description,\n ...(institutionId ? { institutionId } : {}),\n ...(requesterName ? { requesterName } : {}),\n ...(email ? { email } : {}),\n // The app the chat runs in, when it is one the API knows\n ...(isSupportTicketOrigin(channel) ? { origin: channel } : {}),\n });\n const ticketId = data.data.id;\n sessions.set(ticketId, {\n token: data.data.guestToken,\n name: requesterName || 'Visitante',\n });\n\n // The first message is saved with the ticket; read it back for its id.\n // /support/external takes no files, so they go as the next message.\n let messages = await listMessages(ticketId);\n if (!message && messages[0]) {\n hiddenIds.add(messages[0].id);\n messages = messages.slice(1);\n }\n if (files.length) messages.push(await sendMessage(ticketId, '', files));\n return {\n ticket: {\n id: ticketId,\n status: SUPPORT_CHAT_STATUS.ABERTO,\n handlerType: null,\n problemType: null,\n },\n messages,\n };\n },\n\n sendMessage,\n\n async getArticle(institutionId, articleId) {\n const { data } = await apiClient.get<{\n data: { article: SupportChatArticle };\n }>(`/help-center/${institutionId}/articles/${articleId}`);\n const { id, title, body, updatedAt } = data.data.article;\n return { id, title, body, updatedAt: updatedAt ?? null };\n },\n\n async updateContact(ticketId, contact) {\n if (!apiClient.put) {\n throw new Error('O cliente da API não envia PUT');\n }\n await apiClient.put(`/support/guest/${ticketId}`, contact, {\n headers: headersFor(ticketId),\n });\n const session = sessions.get(ticketId);\n if (session && contact.requesterName) {\n session.name = contact.requesterName;\n }\n },\n\n listMessages,\n\n subscribe(ticketId, listener) {\n // id → what the chat shows besides the text; a message seen again with\n // more of it (a stale WebSocket history may lack contactRequest/faq) is\n // delivered again so the chat replaces it\n const seen = new Map<string, string>();\n const extrasOf = (message: SupportChatMessage) =>\n JSON.stringify([message.contactRequest ?? null, message.faq ?? null]);\n const NO_EXTRAS = JSON.stringify([null, null]);\n let socket: WebSocket | null = null;\n let pollTimer: ReturnType<typeof setInterval> | null = null;\n let active = true;\n\n const deliver = (message: SupportChatMessage) => {\n if (!isVisible(message)) return;\n const extras = extrasOf(message);\n const previous = seen.get(message.id);\n // Same as before, or a poorer copy: nothing new to show\n if (previous === extras || (previous && extras === NO_EXTRAS)) return;\n seen.set(message.id, extras);\n listener({ type: 'new_message', payload: { message } });\n };\n\n const poll = async () => {\n try {\n (await listMessages(ticketId)).forEach(deliver);\n } catch {\n // Next tick tries again\n }\n };\n const startPolling = () => {\n if (pollTimer || !active) return;\n void poll();\n pollTimer = setInterval(poll, pollIntervalMs);\n };\n const stopPolling = () => {\n if (pollTimer) clearInterval(pollTimer);\n pollTimer = null;\n };\n\n const handleFrame = (raw: string) => {\n let event: { type?: string; payload?: Record<string, unknown> };\n try {\n event = JSON.parse(raw);\n } catch {\n return;\n }\n const payload = event.payload ?? {};\n if (event.type === 'history') {\n (payload.messages as SupportChatMessage[]).forEach((message) =>\n deliver({ ...message, attachments: message.attachments ?? [] })\n );\n } else if (event.type === 'new_message') {\n const message = payload.message as SupportChatMessage;\n deliver({ ...message, attachments: message.attachments ?? [] });\n } else if (\n event.type === 'message_updated' ||\n event.type === 'ticket_updated'\n ) {\n listener(event as SupportChatEvent);\n } else if (event.type === 'error') {\n listener({\n type: 'error',\n payload: {\n message: String(payload.message_text ?? payload.message ?? ''),\n },\n });\n }\n };\n\n const token = sessions.get(ticketId)?.token;\n if (wsUrl && token) {\n const url = new URL(wsUrl);\n url.searchParams.set('channel', 'support');\n url.searchParams.set('roomId', ticketId);\n url.searchParams.set('token', token);\n socket = new WebSocket(url.toString());\n socket.onopen = () => {\n stopPolling();\n socket?.send(JSON.stringify({ type: 'getInitialData' }));\n // The REST API is the source of truth: a WebSocket deployed apart\n // from it may send a history without contactRequest/faq\n void poll();\n };\n socket.onmessage = (event) => handleFrame(String(event.data));\n socket.onclose = startPolling;\n socket.onerror = startPolling;\n } else {\n startPolling();\n }\n\n return () => {\n active = false;\n stopPolling();\n if (socket) {\n socket.onclose = null;\n socket.close();\n }\n };\n },\n };\n};\n","import {\n SUPPORT_CHAT_AUTHOR,\n SUPPORT_CHAT_GUEST_SENDER_ID,\n SUPPORT_CHAT_HANDLER,\n SUPPORT_CHAT_STATUS,\n type SupportChatAdapter,\n type SupportChatEvent,\n type SupportChatMessage,\n type SupportChatTicket,\n} from '../../types/supportChat';\n\nexport interface MockSupportChatAdapterOptions {\n /** Latency of every call, in ms (default 300) */\n delayMs?: number;\n /** Delay of the automatic agent reply, in ms (default 1500) */\n replyDelayMs?: number;\n /** Text of the agent reply; null disables it */\n replyText?: string | null;\n}\n\nconst DEFAULT_REPLY =\n 'Olá! Recebemos sua mensagem e já vamos te ajudar. Este é um atendimento de demonstração.';\n\n/**\n * In-memory adapter for the support chat widget. It mirrors the backend\n * contract (statuses, authors, events) so the widget can be built and tested\n * before the real API + WebSocket adapter exists.\n */\nexport const createMockSupportChatAdapter = ({\n delayMs = 300,\n replyDelayMs = 1500,\n replyText = DEFAULT_REPLY,\n}: MockSupportChatAdapterOptions = {}): SupportChatAdapter => {\n const tickets = new Map<string, SupportChatTicket>();\n const messages = new Map<string, SupportChatMessage[]>();\n const requesters = new Map<string, { name: string; logged: boolean }>();\n const listeners = new Map<string, Set<(event: SupportChatEvent) => void>>();\n let sequence = 0;\n\n const wait = () => new Promise((resolve) => setTimeout(resolve, delayMs));\n const nextId = (prefix: string) => `${prefix}-${++sequence}`;\n\n const emit = (ticketId: string, event: SupportChatEvent) => {\n listeners.get(ticketId)?.forEach((listener) => listener(event));\n };\n\n const append = (ticketId: string, message: SupportChatMessage) => {\n messages.set(ticketId, [...(messages.get(ticketId) ?? []), message]);\n };\n\n const toAttachments = (files: File[]) =>\n files.map((file) => ({\n id: nextId('file'),\n url: URL.createObjectURL(file),\n fileName: file.name,\n mimeType: file.type,\n size: file.size,\n }));\n\n const buildRequesterMessage = (\n ticketId: string,\n content: string,\n files: File[] = []\n ): SupportChatMessage => {\n const requester = requesters.get(ticketId);\n return {\n id: nextId('msg'),\n senderId: requester?.logged ? 'user' : SUPPORT_CHAT_GUEST_SENDER_ID,\n senderName: requester?.name ?? 'Visitante',\n senderPhoto: null,\n senderRole: requester?.logged\n ? SUPPORT_CHAT_AUTHOR.USER\n : SUPPORT_CHAT_AUTHOR.GUEST,\n content,\n messageType: 'text',\n createdAt: new Date().toISOString(),\n attachments: toAttachments(files),\n };\n };\n\n const scheduleReply = (ticketId: string) => {\n if (replyText === null) return;\n setTimeout(() => {\n const ticket = tickets.get(ticketId);\n if (!ticket) return;\n\n const reply: SupportChatMessage = {\n id: nextId('msg'),\n senderId: 'agent',\n senderName: 'Suporte',\n senderPhoto: null,\n senderRole: SUPPORT_CHAT_AUTHOR.AGENT,\n content: replyText,\n messageType: 'text',\n createdAt: new Date().toISOString(),\n attachments: [],\n };\n append(ticketId, reply);\n\n const updated: SupportChatTicket = {\n ...ticket,\n status: SUPPORT_CHAT_STATUS.EM_ANDAMENTO,\n handlerType: SUPPORT_CHAT_HANDLER.HUMANO,\n };\n tickets.set(ticketId, updated);\n\n emit(ticketId, { type: 'new_message', payload: { message: reply } });\n emit(ticketId, {\n type: 'ticket_updated',\n payload: {\n ticketId,\n status: updated.status,\n handlerType: updated.handlerType,\n problemType: updated.problemType,\n },\n });\n }, replyDelayMs);\n };\n\n return {\n async getArticle(_institutionId, articleId) {\n await wait();\n return {\n id: articleId,\n title: 'Como gerar e localizar simulados',\n body:\n '<p>Monte um simulado com as matérias que quiser e acompanhe o resultado. 📝</p>' +\n '<h2>Passo a passo</h2>' +\n '<ol><li><p>No menu, abra <strong>Simulados</strong>.</p></li>' +\n '<li><p>Clique em <strong>Gerar simulado</strong> e escolha as áreas.</p></li>' +\n '<li><p>Responda e veja o resultado em <strong>Histórico</strong>.</p></li></ol>' +\n '<p><a href=\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\">Assista ao vídeo do passo a passo</a></p>' +\n '<blockquote><p>💡 Dica: refaça as questões que errou.</p></blockquote>',\n updatedAt: '2026-10-21T12:00:00.000Z',\n };\n },\n\n async startConversation({ message, requesterName, email, files }) {\n await wait();\n const ticket: SupportChatTicket = {\n id: nextId('ticket'),\n status: SUPPORT_CHAT_STATUS.ABERTO,\n handlerType: null,\n problemType: null,\n };\n tickets.set(ticket.id, ticket);\n requesters.set(ticket.id, {\n name: requesterName || 'Visitante',\n logged: Boolean(email),\n });\n\n const first = buildRequesterMessage(ticket.id, message, files);\n append(ticket.id, first);\n scheduleReply(ticket.id);\n return { ticket, messages: [first] };\n },\n\n async sendMessage(ticketId, content, files) {\n await wait();\n const ticket = tickets.get(ticketId);\n if (!ticket) throw new Error('Atendimento não encontrado');\n if (ticket.status === SUPPORT_CHAT_STATUS.FECHADO) {\n throw new Error('Este atendimento já foi encerrado');\n }\n\n const message = buildRequesterMessage(ticketId, content, files);\n append(ticketId, message);\n scheduleReply(ticketId);\n return message;\n },\n\n async listMessages(ticketId) {\n await wait();\n return [...(messages.get(ticketId) ?? [])];\n },\n\n subscribe(ticketId, listener) {\n const set = listeners.get(ticketId) ?? new Set();\n set.add(listener);\n listeners.set(ticketId, set);\n return () => {\n set.delete(listener);\n };\n },\n };\n};\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","XIcon","useState","useState","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","title","useEffect","useState","jsx","jsxs","useState","useEffect","jsx","jsxs","useState","useRef","useCallback","useEffect","XIcon","useMemo","jsx","useMemo"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-PPYCCT2K.js","../src/components/EnemClassroomStartModal/EnemClassroomStartModal.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-PPYCCT2K.js","sourcesContent":[null,"import { useCallback, useEffect, useMemo, useState, ChangeEvent } from 'react';\nimport Modal from '../Modal/Modal';\nimport Stepper from '../Stepper/Stepper';\nimport Button from '../Button/Button';\nimport Input from '../Input/Input';\nimport CheckBox from '../CheckBox/CheckBox';\nimport Text from '../Text/Text';\nimport VideoPlayer from '../VideoPlayer/VideoPlayer';\nimport { CaretLeftIcon } from '@phosphor-icons/react/dist/csr/CaretLeft';\nimport { CaretRightIcon } from '@phosphor-icons/react/dist/csr/CaretRight';\nimport { CheckIcon } from '@phosphor-icons/react/dist/csr/Check';\nimport { cn } from '../../utils/utils';\nimport warningClassroomIcon from '../../assets/img/enemClassroom/warning-classroom.png';\nimport warningTimerIcon from '../../assets/img/enemClassroom/warning-timer.png';\nimport warningIrreversibleIcon from '../../assets/img/enemClassroom/warning-irreversible.png';\nimport flagEnglish from '../../assets/img/enemClassroom/flag-english.png';\nimport flagSpanish from '../../assets/img/enemClassroom/flag-spanish.png';\nimport surveyCourseIllustration from '../../assets/img/enemClassroom/survey-course.svg';\nimport surveyUniversityIllustration from '../../assets/img/enemClassroom/survey-university.png';\nimport {\n ENEM_CLASSROOM_LANGUAGE,\n ENEM_CLASSROOM_LANGUAGE_LABELS,\n type EnemClassroomExam,\n type EnemClassroomLanguage,\n type EnemClassroomStartPayload,\n type EnemClassroomStudentTexts,\n type EnemClassroomSurveyQuestion,\n} from '../../types/enemClassroom';\n\n/** Title shared by every step, as in the design. */\n/**\n * The three warnings of the introduction step. The exam only counts when\n * taken in class, so they are shown before anything else. The first one is\n * the backoffice's to write (`studentTexts`); the other two are fixed.\n *\n * @param texts - The exam's texts\n * @returns The warnings in order\n */\nconst buildIntroWarnings = (texts: EnemClassroomStudentTexts) => [\n {\n icon: warningClassroomIcon,\n title: texts.introWarningTitle,\n description: texts.introWarningText,\n },\n {\n icon: warningTimerIcon,\n title: 'O cronômetro liga na hora',\n description:\n 'Assim que clicar em começar, o tempo já corre e não para, mesmo se fechar o navegador.',\n },\n {\n icon: warningIrreversibleIcon,\n title: 'Não tem como desfazer',\n description:\n 'Iniciar fora do horário estabelecido pelo seu professor em sala de aula pode atrapalhar seu resultado!',\n },\n];\n\n/** Flag shown next to each language, as in the design. */\nconst LANGUAGE_FLAGS: Record<EnemClassroomLanguage, string> = {\n [ENEM_CLASSROOM_LANGUAGE.INGLES]: flagEnglish,\n [ENEM_CLASSROOM_LANGUAGE.ESPANHOL]: flagSpanish,\n};\n\n/**\n * Illustration of each survey step, in the order the steps are asked: the\n * course question, then the university one.\n *\n * Fixed here rather than sent by the backoffice, so it is keyed by position —\n * reordering the questions there moves the illustration with the position,\n * not with the question. A question past the end simply gets none.\n */\nconst SURVEY_ILLUSTRATIONS = [\n surveyCourseIllustration,\n surveyUniversityIllustration,\n] as const;\n\n/**\n * The white card the warnings and the options sit on. Same vocabulary as\n * SelectionButton, so the modal reads like the rest of the design system\n * against the modal's `secondary-50` background.\n */\nconst CARD_CLASSES =\n 'rounded-xl border border-border-50 bg-background shadow-soft-shadow-1';\n\n/**\n * Gradiente da identidade da simulação em sala de aula — os mesmos tons do\n * card do painel do aluno. Não sai dos tokens de tema porque é a marca da\n * prova, não a da instituição.\n *\n * Literal, e não `bg-gradient-to-r from-… to-…`: o Tailwind v4 interpola\n * `in oklab`, o que muda os tons do meio em relação ao sRGB do design.\n *\n * `border-0` porque o `solid/primary` do Button traz `border-primary-950`, que\n * o gradiente não cobre — ele só troca o fundo. Zerar a largura, em vez de\n * pintar a borda de transparente, também neutraliza as cores de hover e\n * active, que voltariam a aparecer.\n */\nconst GRADIENT_CTA_CLASSES =\n 'bg-[linear-gradient(270deg,#B21FF6_0%,#F95493_100%)] border-0 hover:opacity-90 transition-opacity';\n\n/**\n * One selectable option: a card carrying an optional icon and a label.\n *\n * The radio control itself is visually hidden — in the design the card's own\n * border carries the selection. The native input stays in the tree so the\n * options remain a real radio group: arrow keys move between them and screen\n * readers announce \"1 of 2\", which a set of toggle buttons would not.\n */\nconst OptionCard = ({\n name,\n value,\n label,\n iconSrc,\n checked,\n onSelect,\n testId,\n}: {\n name: string;\n value: string;\n label: string;\n iconSrc?: string;\n checked: boolean;\n onSelect: () => void;\n testId?: string;\n}) => (\n <label\n className={cn(\n CARD_CLASSES,\n 'flex flex-row items-center gap-3 px-4 py-3.5',\n 'cursor-pointer transition-colors hover:bg-background-100',\n 'focus-within:ring-2 focus-within:ring-indicator-info',\n checked && 'border-primary-700 ring-1 ring-primary-700'\n )}\n >\n <input\n type=\"radio\"\n name={name}\n value={value}\n checked={checked}\n onChange={onSelect}\n className=\"sr-only\"\n data-testid={testId}\n />\n {iconSrc && (\n <img src={iconSrc} alt=\"\" aria-hidden className=\"w-6 h-6 flex-shrink-0\" />\n )}\n <Text size=\"sm\" weight=\"semibold\" className=\"text-text-900\">\n {label}\n </Text>\n </label>\n);\n\n/**\n * How a survey question was answered. The student either writes an answer or\n * ticks \"ainda estou pensando\" — the tick clears and disables the field, so\n * the two never disagree.\n */\ninterface SurveyAnswerDraft {\n skipped: boolean;\n text: string;\n}\n\n/**\n * One line of the summary: the step's name over what the student answered.\n */\nconst SummaryRow = ({ label, value }: { label: string; value: string }) => (\n <div className=\"flex flex-col rounded-lg bg-primary-50 px-3 py-2\">\n <Text as=\"dt\" size=\"sm\" weight=\"bold\" className=\"text-primary-800\">\n {label}\n </Text>\n <Text as=\"dd\" size=\"sm\" className=\"text-text-600\">\n {value}\n </Text>\n </div>\n);\n\n/** The introduction is always first; the first choice comes right after it. */\nconst INTRO_STEP = 0;\nconst FIRST_CHOICE_STEP = 1;\n\n/** One screen of the flow. Which ones exist depends on the exam. */\ntype FlowStep =\n | { kind: 'intro' }\n | { kind: 'language' }\n | { kind: 'survey'; question: EnemClassroomSurveyQuestion; index: number }\n | { kind: 'summary' };\n\nexport interface EnemClassroomStartModalProps {\n isOpen: boolean;\n onClose: () => void;\n exam: EnemClassroomExam;\n /** Called with everything the start endpoint needs. */\n onStart: (payload: EnemClassroomStartPayload) => void;\n /** Disables the final button while the start request is in flight. */\n isStarting?: boolean;\n}\n\n/**\n * Whether a survey draft is complete: skipped, or answered with some text.\n */\nconst isSurveyAnswerComplete = (draft: SurveyAnswerDraft | undefined) =>\n draft?.skipped === true || (draft?.text.trim().length ?? 0) > 0;\n\n/**\n * The text shown for a survey answer on the summary screen.\n */\nconst describeSurveyAnswer = (\n question: EnemClassroomSurveyQuestion,\n draft: SurveyAnswerDraft | undefined\n) => (draft && !draft.skipped ? draft.text.trim() : question.skipLabel);\n\n/**\n * Start flow of the in-classroom ENEM simulation.\n *\n * Steps, in order: introduction (video + warnings), foreign language — only\n * when the exam offers the choice —, one step per survey question, and a\n * summary the student confirms before the exam opens. The stepper only lists\n * the steps that carry a choice; the summary is a screen of its own, as in\n * the design.\n *\n * Presentational: it collects the choices and hands them to `onStart`; the\n * caller talks to the API.\n *\n * @example\n * ```tsx\n * <EnemClassroomStartModal\n * isOpen={open}\n * onClose={() => setOpen(false)}\n * exam={exam}\n * onStart={(payload) => start(exam.id, payload)}\n * />\n * ```\n */\nconst EnemClassroomStartModal = ({\n isOpen,\n onClose,\n exam,\n onStart,\n isStarting = false,\n}: EnemClassroomStartModalProps) => {\n const surveyQuestions = useMemo(\n () => [...exam.surveyQuestions].sort((a, b) => a.position - b.position),\n [exam.surveyQuestions]\n );\n // The screens this exam has: no language step on an exam without the\n // choice, so the student goes from the warnings straight to the survey.\n const steps = useMemo<FlowStep[]>(\n () => [\n { kind: 'intro' },\n ...(exam.languageChoice ? [{ kind: 'language' } as const] : []),\n ...surveyQuestions.map((question, index) => ({\n kind: 'survey' as const,\n question,\n index,\n })),\n { kind: 'summary' },\n ],\n [exam.languageChoice, surveyQuestions]\n );\n const summaryStep = steps.length - 1;\n\n const [step, setStep] = useState(INTRO_STEP);\n const [language, setLanguage] = useState<EnemClassroomLanguage | null>(null);\n const [answers, setAnswers] = useState<Record<string, SurveyAnswerDraft>>({});\n\n // Every opening starts from the introduction with nothing filled in — and\n // so does a different exam handed in while the modal is open, whose steps\n // and questions are not the ones the answers were given to.\n useEffect(() => {\n if (isOpen) {\n setStep(INTRO_STEP);\n setLanguage(null);\n setAnswers({});\n }\n }, [isOpen, exam.id]);\n\n const stepperSteps = useMemo(\n () =>\n steps\n .filter((flowStep) => flowStep.kind !== 'summary')\n .map((flowStep) => {\n if (flowStep.kind === 'intro') {\n return {\n id: 'intro',\n label: 'Introdução',\n state: 'pending' as const,\n };\n }\n if (flowStep.kind === 'language') {\n return {\n id: 'language',\n label: 'Língua estrangeira',\n state: 'pending' as const,\n };\n }\n return {\n id: flowStep.question.id,\n label: flowStep.question.stepLabel,\n state: 'pending' as const,\n };\n }),\n [steps]\n );\n\n const currentStep = steps[step] ?? steps[INTRO_STEP];\n const currentQuestion =\n currentStep.kind === 'survey' ? currentStep.question : null;\n\n const canGoNext =\n currentStep.kind === 'language'\n ? language !== null\n : currentQuestion !== null &&\n isSurveyAnswerComplete(answers[currentQuestion.id]);\n\n const updateAnswer = useCallback(\n (questionId: string, patch: Partial<SurveyAnswerDraft>) => {\n setAnswers((prev) => {\n const current = prev[questionId] ?? { skipped: false, text: '' };\n return { ...prev, [questionId]: { ...current, ...patch } };\n });\n },\n []\n );\n\n const handleStart = useCallback(() => {\n // The language is only a choice on an exam that offers one.\n if (exam.languageChoice && !language) return;\n onStart({\n language: exam.languageChoice ? language : null,\n surveyAnswers: surveyQuestions.map((question) => {\n const draft = answers[question.id];\n const text = draft && !draft.skipped ? draft.text.trim() : '';\n return { questionId: question.id, answer: text || null };\n }),\n });\n }, [exam.languageChoice, language, surveyQuestions, answers, onStart]);\n\n const renderIntroStep = () => (\n <div className=\"flex flex-col gap-6\" data-testid=\"enem-classroom-intro\">\n {exam.videoUrl && (\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-950\"\n data-testid=\"enem-classroom-intro-video-text\"\n >\n {exam.studentTexts.introVideoText}\n </Text>\n )}\n {exam.videoUrl && (\n <VideoPlayer\n src={exam.videoUrl}\n title={exam.title}\n // O desenho mostra só o vídeo: o nome da prova já está no título do\n // modal. O `title` fica para rotular o player nos leitores de tela.\n hideHeader\n className=\"w-full rounded-xl overflow-hidden\"\n autoSave={false}\n />\n )}\n\n <div className=\"flex flex-col gap-4\">\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n {exam.studentTexts.introHeading}\n </Text>\n {buildIntroWarnings(exam.studentTexts).map(\n ({ icon, title, description }) => (\n <div\n key={title}\n className={cn(\n CARD_CLASSES,\n 'flex flex-row gap-4 items-start p-4'\n )}\n >\n <img\n src={icon}\n alt=\"\"\n aria-hidden\n className=\"flex-shrink-0 w-10 h-10\"\n />\n <div className=\"flex flex-col gap-1\">\n <Text size=\"md\" weight=\"bold\" className=\"text-text-950\">\n {title}\n </Text>\n <Text size=\"sm\" className=\"text-text-600\">\n {description}\n </Text>\n </div>\n </div>\n )\n )}\n </div>\n </div>\n );\n\n const renderLanguageStep = () => (\n <div className=\"flex flex-col gap-4\" data-testid=\"enem-classroom-language\">\n <Text\n id=\"enem-classroom-language-prompt\"\n size=\"lg\"\n weight=\"bold\"\n className=\"text-text-950\"\n >\n Qual língua estrangeira você vai usar na simulação?\n </Text>\n <div\n role=\"radiogroup\"\n aria-labelledby=\"enem-classroom-language-prompt\"\n className=\"flex flex-col gap-4\"\n >\n {Object.values(ENEM_CLASSROOM_LANGUAGE).map((value) => (\n <OptionCard\n key={value}\n name=\"enem-classroom-language\"\n value={value}\n label={ENEM_CLASSROOM_LANGUAGE_LABELS[value]}\n iconSrc={LANGUAGE_FLAGS[value]}\n checked={language === value}\n onSelect={() => setLanguage(value)}\n testId={`enem-classroom-language-${value.toLowerCase()}`}\n />\n ))}\n </div>\n </div>\n );\n\n const renderSurveyStep = (\n question: EnemClassroomSurveyQuestion,\n index: number\n ) => {\n const draft = answers[question.id];\n const illustration = SURVEY_ILLUSTRATIONS[index];\n // The two ways of answering disable each other, so they can never\n // contradict — and either one on its own releases the step.\n const hasText = (draft?.text.trim().length ?? 0) > 0;\n const isSkipped = draft?.skipped === true;\n return (\n <div\n className=\"flex flex-col gap-4\"\n data-testid={`enem-classroom-survey-${question.id}`}\n >\n {illustration && (\n <img\n src={illustration}\n alt=\"\"\n aria-hidden\n className=\"w-[220px] max-w-full self-center\"\n data-testid=\"enem-classroom-survey-illustration\"\n />\n )}\n <Text\n id={`enem-classroom-survey-prompt-${question.id}`}\n size=\"lg\"\n weight=\"bold\"\n className=\"text-text-950\"\n >\n {question.prompt}\n </Text>\n <div className=\"flex flex-col gap-2\">\n <Text\n as=\"label\"\n htmlFor={`enem-classroom-survey-input-${question.id}`}\n size=\"sm\"\n className=\"text-text-600\"\n >\n {question.inputLabel}\n </Text>\n <Input\n id={`enem-classroom-survey-input-${question.id}`}\n variant=\"rounded\"\n placeholder={question.inputPlaceholder}\n value={draft?.text ?? ''}\n maxLength={255}\n disabled={isSkipped}\n onChange={(event: ChangeEvent<HTMLInputElement>) =>\n updateAnswer(question.id, { text: event.target.value })\n }\n data-testid=\"enem-classroom-survey-input\"\n />\n </div>\n\n <CheckBox\n label={question.skipLabel}\n checked={isSkipped}\n disabled={hasText}\n onChange={(event: ChangeEvent<HTMLInputElement>) =>\n updateAnswer(question.id, { skipped: event.target.checked })\n }\n data-testid=\"enem-classroom-survey-skip\"\n />\n </div>\n );\n };\n\n const renderSummaryStep = () => (\n <div\n className=\"flex flex-col items-center gap-6 text-center\"\n data-testid=\"enem-classroom-summary\"\n >\n <span className=\"flex items-center justify-center w-14 h-14 rounded-full bg-success-200 text-success-950\">\n <CheckIcon size={24} aria-hidden />\n </span>\n <div className=\"flex flex-col gap-2\">\n <Text size=\"xl\" weight=\"bold\" className=\"text-text-950\">\n Boa sorte!\n </Text>\n <Text size=\"sm\" className=\"text-text-600\">\n O seu simulado ENEM já vai começar e o cronômetro vai iniciar. Respire\n fundo e dê o seu melhor!\n </Text>\n </div>\n\n <div\n className={cn(CARD_CLASSES, 'flex flex-col gap-3 w-full text-left p-4')}\n >\n <Text size=\"sm\" weight=\"bold\" className=\"text-text-950\">\n Suas respostas\n </Text>\n <dl className=\"flex flex-col gap-2\">\n {exam.languageChoice && (\n <SummaryRow\n label=\"Idioma\"\n value={language ? ENEM_CLASSROOM_LANGUAGE_LABELS[language] : '—'}\n />\n )}\n {surveyQuestions.map((question) => (\n <SummaryRow\n key={question.id}\n label={question.stepLabel}\n value={describeSurveyAnswer(question, answers[question.id])}\n />\n ))}\n </dl>\n <Button\n size=\"medium\"\n variant=\"outline\"\n action=\"primary\"\n className=\"self-center\"\n disabled={isStarting}\n onClick={() => setStep(FIRST_CHOICE_STEP)}\n data-testid=\"enem-classroom-review\"\n >\n Revisar minhas respostas\n </Button>\n </div>\n </div>\n );\n\n const renderStepContent = () => {\n if (currentStep.kind === 'intro') return renderIntroStep();\n if (currentStep.kind === 'language') return renderLanguageStep();\n if (currentStep.kind === 'survey')\n return renderSurveyStep(currentStep.question, currentStep.index);\n return renderSummaryStep();\n };\n\n const showNavigation = step >= FIRST_CHOICE_STEP && step < summaryStep;\n const showStepper = step < summaryStep;\n\n /*\n * O rodapé do Modal não rola, e é lá que o desenho quer o botão: na\n * introdução ele leva o CTA; nos passos de escolha, a navegação.\n */\n /**\n * O rodapé do Modal não rola, e é onde o desenho quer o botão de cada passo:\n * o CTA na introdução, a navegação nos passos de escolha e o de iniciar no\n * resumo. O \"Revisar minhas respostas\" fica de fora de propósito — ele mora\n * dentro do card de respostas.\n * @returns O rodapé do passo atual, ou nada quando ele não tem botão\n */\n const renderFooter = () => {\n if (currentStep.kind === 'intro') {\n return (\n <Button\n size=\"large\"\n variant=\"solid\"\n action=\"primary\"\n className={cn('w-full', GRADIENT_CTA_CLASSES)}\n onClick={() => setStep(FIRST_CHOICE_STEP)}\n data-testid=\"enem-classroom-intro-continue\"\n >\n Estou em sala. Quero começar!\n </Button>\n );\n }\n\n if (currentStep.kind === 'summary') {\n return (\n <Button\n size=\"large\"\n variant=\"solid\"\n action=\"primary\"\n className={cn('w-full', GRADIENT_CTA_CLASSES)}\n disabled={isStarting}\n onClick={handleStart}\n data-testid=\"enem-classroom-start\"\n >\n Iniciar a prova!\n </Button>\n );\n }\n\n return showNavigation ? (\n <div className=\"flex flex-row justify-end gap-2 w-full\">\n <Button\n size=\"medium\"\n variant=\"outline\"\n action=\"primary\"\n iconLeft={<CaretLeftIcon size={16} aria-hidden />}\n onClick={() => setStep((current) => current - 1)}\n data-testid=\"enem-classroom-previous\"\n >\n Anterior\n </Button>\n <Button\n size=\"medium\"\n variant=\"solid\"\n action=\"primary\"\n iconRight={<CaretRightIcon size={16} aria-hidden />}\n disabled={!canGoNext}\n onClick={() => setStep((current) => current + 1)}\n data-testid=\"enem-classroom-next\"\n >\n Próximo\n </Button>\n </div>\n ) : undefined;\n };\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={onClose}\n /*\n * A tela de resumo abre com o header vazio, só o X — quem dá o título\n * ali é o \"Boa sorte!\". O texto continua no <h2>, apenas oculto: o\n * `aria-labelledby` do Modal aponta pra ele, e um h2 vazio deixaria o\n * dialog sem nome acessível.\n */\n title={\n showStepper ? exam.title : <span className=\"sr-only\">{exam.title}</span>\n }\n size=\"lg\"\n footer={renderFooter()}\n /*\n * A rolagem sai do Modal e vai para o miolo: título e stepper ficam\n * parados em cima, o rodapé embaixo, e só o conteúdo entre eles corre.\n */\n contentClassName=\"flex flex-col overflow-hidden\"\n >\n <div className=\"flex flex-col gap-6 flex-1 min-h-0\">\n {showStepper && (\n <Stepper\n steps={stepperSteps}\n currentStep={step}\n size=\"small\"\n className=\"flex-none\"\n />\n )}\n <div className=\"flex-1 min-h-0 overflow-y-auto\">\n {renderStepContent()}\n </div>\n </div>\n </Modal>\n );\n};\n\nexport default EnemClassroomStartModal;\n"]}