analytica-frontend-lib 1.6.76 → 1.6.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AccessReportModal/index.js +4 -5
- package/dist/AccessReportModal/index.js.map +1 -1
- package/dist/AccessReportModal/index.mjs +3 -4
- package/dist/AccessibilityWidget/index.js +4 -5
- package/dist/AccessibilityWidget/index.js.map +1 -1
- package/dist/AccessibilityWidget/index.mjs +3 -4
- package/dist/Accordation/index.js +4 -4
- package/dist/Accordation/index.mjs +3 -3
- package/dist/ActivitiesHistory/index.js +48 -49
- package/dist/ActivitiesHistory/index.js.map +1 -1
- package/dist/ActivitiesHistory/index.mjs +47 -48
- package/dist/ActivityCardQuestionBanks/index.d.ts.map +1 -1
- package/dist/ActivityCardQuestionBanks/index.js +48 -49
- package/dist/ActivityCardQuestionBanks/index.js.map +1 -1
- package/dist/ActivityCardQuestionBanks/index.mjs +47 -48
- package/dist/ActivityCardQuestionPreview/index.js +48 -49
- package/dist/ActivityCardQuestionPreview/index.js.map +1 -1
- package/dist/ActivityCardQuestionPreview/index.mjs +47 -48
- package/dist/ActivityCreate/ActivityCreateContent/index.js +48 -49
- package/dist/ActivityCreate/ActivityCreateContent/index.js.map +1 -1
- package/dist/ActivityCreate/ActivityCreateContent/index.mjs +47 -48
- package/dist/ActivityCreate/index.js +48 -49
- package/dist/ActivityCreate/index.js.map +1 -1
- package/dist/ActivityCreate/index.mjs +47 -48
- package/dist/ActivityDetails/index.js +48 -49
- package/dist/ActivityDetails/index.js.map +1 -1
- package/dist/ActivityDetails/index.mjs +47 -48
- package/dist/ActivityFilters/index.js +48 -49
- package/dist/ActivityFilters/index.js.map +1 -1
- package/dist/ActivityFilters/index.mjs +47 -48
- package/dist/ActivityPageLayout/index.js +48 -49
- package/dist/ActivityPageLayout/index.js.map +1 -1
- package/dist/ActivityPageLayout/index.mjs +47 -48
- package/dist/ActivityPreview/index.js +48 -49
- package/dist/ActivityPreview/index.js.map +1 -1
- package/dist/ActivityPreview/index.mjs +47 -48
- package/dist/AlertDialog/index.d.ts.map +1 -1
- package/dist/AlertDialog/index.js +3 -3
- package/dist/AlertDialog/index.mjs +2 -2
- package/dist/AlertManager/index.js +48 -49
- package/dist/AlertManager/index.js.map +1 -1
- package/dist/AlertManager/index.mjs +47 -48
- package/dist/AlertManagerView/index.js +4 -5
- package/dist/AlertManagerView/index.js.map +1 -1
- package/dist/AlertManagerView/index.mjs +3 -4
- package/dist/AnnouncementModal/index.js +4 -5
- package/dist/AnnouncementModal/index.js.map +1 -1
- package/dist/AnnouncementModal/index.mjs +3 -4
- package/dist/AnnouncementsGate/index.js +5 -6
- package/dist/AnnouncementsGate/index.js.map +1 -1
- package/dist/AnnouncementsGate/index.mjs +4 -5
- package/dist/AnswerKeyModal/index.js +4 -5
- package/dist/AnswerKeyModal/index.js.map +1 -1
- package/dist/AnswerKeyModal/index.mjs +3 -4
- package/dist/AppHeader/index.js +8 -9
- package/dist/AppHeader/index.js.map +1 -1
- package/dist/AppHeader/index.mjs +7 -8
- package/dist/CalendarCard/index.js +6 -7
- package/dist/CalendarCard/index.js.map +1 -1
- package/dist/CalendarCard/index.mjs +5 -6
- package/dist/Card/index.d.ts.map +1 -1
- package/dist/Card/index.js +3 -3
- package/dist/Card/index.mjs +2 -2
- package/dist/Chat/index.js +4 -5
- package/dist/Chat/index.js.map +1 -1
- package/dist/Chat/index.mjs +3 -4
- package/dist/Chatbot/index.js +4 -5
- package/dist/Chatbot/index.js.map +1 -1
- package/dist/Chatbot/index.mjs +3 -4
- package/dist/CheckBoxGroup/index.js +48 -49
- package/dist/CheckBoxGroup/index.js.map +1 -1
- package/dist/CheckBoxGroup/index.mjs +47 -48
- package/dist/ComparatorChart/ComparatorView/index.js +4 -5
- package/dist/ComparatorChart/ComparatorView/index.js.map +1 -1
- package/dist/ComparatorChart/ComparatorView/index.mjs +3 -4
- package/dist/CorrectActivityModal/index.js +9 -10
- package/dist/CorrectActivityModal/index.js.map +1 -1
- package/dist/CorrectActivityModal/index.mjs +8 -9
- package/dist/DownloadButton/index.js +6 -7
- package/dist/DownloadButton/index.js.map +1 -1
- package/dist/DownloadButton/index.mjs +5 -6
- package/dist/DownloadModal/index.js +4 -5
- package/dist/DownloadModal/index.js.map +1 -1
- package/dist/DownloadModal/index.mjs +3 -4
- package/dist/DropdownMenu/index.js +4 -5
- package/dist/DropdownMenu/index.js.map +1 -1
- package/dist/DropdownMenu/index.mjs +3 -4
- package/dist/EnemClassroomStartModal/index.js +8 -9
- package/dist/EnemClassroomStartModal/index.js.map +1 -1
- package/dist/EnemClassroomStartModal/index.mjs +7 -8
- package/dist/EnemMoment/index.js +110 -94
- package/dist/EnemMoment/index.js.map +1 -1
- package/dist/EnemMoment/index.mjs +74 -58
- package/dist/EnemMoment/index.mjs.map +1 -1
- package/dist/EnemMomentStudentModal/index.js +62 -63
- package/dist/EnemMomentStudentModal/index.js.map +1 -1
- package/dist/EnemMomentStudentModal/index.mjs +48 -49
- package/dist/EnemMomentStudentModal/index.mjs.map +1 -1
- package/dist/EssayThemePicker/index.js +3 -3
- package/dist/EssayThemePicker/index.mjs +2 -2
- package/dist/ExamDetailsLayout/ExamDetailsPage/index.js +48 -49
- package/dist/ExamDetailsLayout/ExamDetailsPage/index.js.map +1 -1
- package/dist/ExamDetailsLayout/ExamDetailsPage/index.mjs +47 -48
- package/dist/ExamPageLayout/index.js +48 -49
- package/dist/ExamPageLayout/index.js.map +1 -1
- package/dist/ExamPageLayout/index.mjs +47 -48
- package/dist/Forum/index.js +6 -7
- package/dist/Forum/index.js.map +1 -1
- package/dist/Forum/index.mjs +5 -6
- package/dist/IconButton/index.js +2 -2
- package/dist/IconButton/index.mjs +1 -1
- package/dist/LessonCardPreview/index.js +48 -49
- package/dist/LessonCardPreview/index.js.map +1 -1
- package/dist/LessonCardPreview/index.mjs +47 -48
- package/dist/LessonMediaSections/index.js +48 -49
- package/dist/LessonMediaSections/index.js.map +1 -1
- package/dist/LessonMediaSections/index.mjs +47 -48
- package/dist/LessonsCatalog/index.js +48 -49
- package/dist/LessonsCatalog/index.js.map +1 -1
- package/dist/LessonsCatalog/index.mjs +47 -48
- package/dist/Modal/index.d.ts +11 -1
- package/dist/Modal/index.d.ts.map +1 -1
- package/dist/Modal/index.js +3 -4
- package/dist/Modal/index.js.map +1 -1
- package/dist/Modal/index.mjs +2 -3
- package/dist/MultiCheckSelect/index.js +5 -6
- package/dist/MultiCheckSelect/index.js.map +1 -1
- package/dist/MultiCheckSelect/index.mjs +4 -5
- package/dist/NotificationCard/index.js +6 -7
- package/dist/NotificationCard/index.js.map +1 -1
- package/dist/NotificationCard/index.mjs +5 -6
- package/dist/PerformanceReportModal/index.js +4 -5
- package/dist/PerformanceReportModal/index.js.map +1 -1
- package/dist/PerformanceReportModal/index.mjs +3 -4
- package/dist/PrintableUsersTable/index.js +48 -49
- package/dist/PrintableUsersTable/index.js.map +1 -1
- package/dist/PrintableUsersTable/index.mjs +47 -48
- package/dist/ProgressModal/index.js +4 -5
- package/dist/ProgressModal/index.js.map +1 -1
- package/dist/ProgressModal/index.mjs +3 -4
- package/dist/QuestionsPerformanceCard/index.js +49 -50
- package/dist/QuestionsPerformanceCard/index.js.map +1 -1
- package/dist/QuestionsPerformanceCard/index.mjs +48 -49
- package/dist/Quiz/QuizResult/index.js +4 -4
- package/dist/Quiz/QuizResult/index.mjs +3 -3
- package/dist/Quiz/index.d.ts.map +1 -1
- package/dist/Quiz/index.js +8 -9
- package/dist/Quiz/index.js.map +1 -1
- package/dist/Quiz/index.mjs +7 -8
- package/dist/QuizTimer/index.d.ts +12 -0
- package/dist/QuizTimer/index.d.ts.map +1 -1
- package/dist/QuizTimer/index.js +4 -2
- package/dist/QuizTimer/index.js.map +1 -1
- package/dist/QuizTimer/index.mjs +4 -2
- package/dist/RecommendedLessonCreate/index.js +48 -49
- package/dist/RecommendedLessonCreate/index.js.map +1 -1
- package/dist/RecommendedLessonCreate/index.mjs +47 -48
- package/dist/RecommendedLessonDetails/index.js +11 -12
- package/dist/RecommendedLessonDetails/index.js.map +1 -1
- package/dist/RecommendedLessonDetails/index.mjs +10 -11
- package/dist/RecommendedLessonsHistory/index.js +48 -49
- package/dist/RecommendedLessonsHistory/index.js.map +1 -1
- package/dist/RecommendedLessonsHistory/index.mjs +47 -48
- package/dist/ReportDetailModal/index.js +5 -6
- package/dist/ReportDetailModal/index.js.map +1 -1
- package/dist/ReportDetailModal/index.mjs +4 -5
- package/dist/RichEditor/index.js +6 -7
- package/dist/RichEditor/index.js.map +1 -1
- package/dist/RichEditor/index.mjs +5 -6
- package/dist/{RichEditorCore-62NIM7PC.js → RichEditorCore-6OT3VNJR.js} +15 -16
- package/dist/RichEditorCore-6OT3VNJR.js.map +1 -0
- package/dist/{RichEditorCore-CCO3SSSU.mjs → RichEditorCore-IF6JQHEO.mjs} +6 -7
- package/dist/{RichEditorCore-CCO3SSSU.mjs.map → RichEditorCore-IF6JQHEO.mjs.map} +1 -1
- package/dist/Search/index.js +5 -6
- package/dist/Search/index.js.map +1 -1
- package/dist/Search/index.mjs +4 -5
- package/dist/SendActivityModal/SendActivityModal.js +48 -49
- package/dist/SendActivityModal/SendActivityModal.js.map +1 -1
- package/dist/SendActivityModal/SendActivityModal.mjs +47 -48
- package/dist/SendActivityModal/index.js +48 -49
- package/dist/SendActivityModal/index.js.map +1 -1
- package/dist/SendActivityModal/index.mjs +47 -48
- package/dist/SendLessonModal/index.js +48 -49
- package/dist/SendLessonModal/index.js.map +1 -1
- package/dist/SendLessonModal/index.mjs +47 -48
- package/dist/SimulatedPerformance/SimulatedPerformanceView/index.js +48 -49
- package/dist/SimulatedPerformance/SimulatedPerformanceView/index.js.map +1 -1
- package/dist/SimulatedPerformance/SimulatedPerformanceView/index.mjs +47 -48
- package/dist/SimulatedPerformance/useSimulatedPerformance/index.js +6 -7
- package/dist/SimulatedPerformance/useSimulatedPerformance/index.js.map +1 -1
- package/dist/SimulatedPerformance/useSimulatedPerformance/index.mjs +5 -6
- package/dist/SimulatedStudentSimulationsModal/index.js +8 -9
- package/dist/SimulatedStudentSimulationsModal/index.js.map +1 -1
- package/dist/SimulatedStudentSimulationsModal/index.mjs +7 -8
- package/dist/SimulationSummaryCards/index.js +5 -5
- package/dist/SimulationSummaryCards/index.mjs +4 -4
- package/dist/SimulationsPage/index.js +48 -49
- package/dist/SimulationsPage/index.js.map +1 -1
- package/dist/SimulationsPage/index.mjs +47 -48
- package/dist/StudentLessonProgressModal/index.js +6 -7
- package/dist/StudentLessonProgressModal/index.js.map +1 -1
- package/dist/StudentLessonProgressModal/index.mjs +5 -6
- package/dist/StudentPerformanceDetailsModal/index.js +9 -10
- package/dist/StudentPerformanceDetailsModal/index.js.map +1 -1
- package/dist/StudentPerformanceDetailsModal/index.mjs +8 -9
- package/dist/Support/TicketModal/index.js +4 -5
- package/dist/Support/TicketModal/index.js.map +1 -1
- package/dist/Support/TicketModal/index.mjs +3 -4
- package/dist/Support/index.js +5 -6
- package/dist/Support/index.js.map +1 -1
- package/dist/Support/index.mjs +4 -5
- package/dist/TableProvider/index.js +48 -49
- package/dist/TableProvider/index.js.map +1 -1
- package/dist/TableProvider/index.mjs +47 -48
- package/dist/UnifiedDraftModelPage/index.js +48 -49
- package/dist/UnifiedDraftModelPage/index.js.map +1 -1
- package/dist/UnifiedDraftModelPage/index.mjs +47 -48
- package/dist/UnifiedHistoryPage/index.js +48 -49
- package/dist/UnifiedHistoryPage/index.js.map +1 -1
- package/dist/UnifiedHistoryPage/index.mjs +47 -48
- package/dist/VideoPlayer/index.js +7 -8
- package/dist/VideoPlayer/index.js.map +1 -1
- package/dist/VideoPlayer/index.mjs +6 -7
- package/dist/Whiteboard/index.js +4 -5
- package/dist/Whiteboard/index.js.map +1 -1
- package/dist/Whiteboard/index.mjs +3 -4
- package/dist/{chunk-EVBQNVQW.mjs → chunk-233FSBU7.mjs} +5 -5
- package/dist/{chunk-JU63EMZM.js → chunk-24DQFREC.js} +3 -3
- package/dist/{chunk-JU63EMZM.js.map → chunk-24DQFREC.js.map} +1 -1
- package/dist/{chunk-QFXSQKW3.mjs → chunk-254EBPMW.mjs} +22 -22
- package/dist/{chunk-QFXSQKW3.mjs.map → chunk-254EBPMW.mjs.map} +1 -1
- package/dist/{chunk-SFQVZ4KQ.js → chunk-2ETPIOCD.js} +3 -3
- package/dist/{chunk-SFQVZ4KQ.js.map → chunk-2ETPIOCD.js.map} +1 -1
- package/dist/{chunk-RCNIBRZY.js → chunk-2VTJ2PKF.js} +10 -10
- package/dist/{chunk-RCNIBRZY.js.map → chunk-2VTJ2PKF.js.map} +1 -1
- package/dist/{chunk-B35D5S4G.js → chunk-2ZMGNCMI.js} +6 -6
- package/dist/{chunk-B35D5S4G.js.map → chunk-2ZMGNCMI.js.map} +1 -1
- package/dist/{chunk-QEPKR6ZF.js → chunk-3KSRDIS2.js} +20 -20
- package/dist/{chunk-QEPKR6ZF.js.map → chunk-3KSRDIS2.js.map} +1 -1
- package/dist/{chunk-MUCV5ZWZ.mjs → chunk-3YF7H577.mjs} +15 -9
- package/dist/chunk-3YF7H577.mjs.map +1 -0
- package/dist/{chunk-MADPP2PW.mjs → chunk-3ZSI2KI5.mjs} +2 -2
- package/dist/{chunk-2W7PTFXS.mjs → chunk-42XKJV56.mjs} +2 -2
- package/dist/{chunk-M6TC6XRQ.mjs → chunk-55ESXRRM.mjs} +2 -2
- package/dist/{chunk-4MURAKPC.mjs → chunk-5DJVOLVX.mjs} +3 -3
- package/dist/{chunk-YVT7CT3G.mjs → chunk-5P6CBDZ6.mjs} +2 -2
- package/dist/{chunk-BHKTJCFD.js → chunk-5QUP5HKB.js} +3 -3
- package/dist/{chunk-BHKTJCFD.js.map → chunk-5QUP5HKB.js.map} +1 -1
- package/dist/{chunk-SFEU7BMY.mjs → chunk-5SFNRXZB.mjs} +2 -2
- package/dist/{chunk-6ALEXGAN.js → chunk-6U4AI5SE.js} +6 -6
- package/dist/{chunk-6ALEXGAN.js.map → chunk-6U4AI5SE.js.map} +1 -1
- package/dist/{chunk-KYY4U5SN.mjs → chunk-6Z5W5J44.mjs} +3 -3
- package/dist/{chunk-XOKBMLW3.js → chunk-7IGUYLSF.js} +3 -3
- package/dist/{chunk-XOKBMLW3.js.map → chunk-7IGUYLSF.js.map} +1 -1
- package/dist/{chunk-GVYGPJH3.mjs → chunk-AU5M2766.mjs} +2 -2
- package/dist/{chunk-2VODGC76.js → chunk-AVEMHDN2.js} +66 -44
- package/dist/chunk-AVEMHDN2.js.map +1 -0
- package/dist/{chunk-HIIOADV4.js → chunk-AZSRKBYS.js} +63 -71
- package/dist/chunk-AZSRKBYS.js.map +1 -0
- package/dist/{chunk-CY2VKNCB.js → chunk-BQIBVC6X.js} +5 -5
- package/dist/{chunk-CY2VKNCB.js.map → chunk-BQIBVC6X.js.map} +1 -1
- package/dist/{chunk-M77TQ5JJ.mjs → chunk-BU32E6BR.mjs} +4 -6
- package/dist/{chunk-M77TQ5JJ.mjs.map → chunk-BU32E6BR.mjs.map} +1 -1
- package/dist/{chunk-5CQVDUDO.js → chunk-BUNF5POU.js} +5 -5
- package/dist/{chunk-5CQVDUDO.js.map → chunk-BUNF5POU.js.map} +1 -1
- package/dist/{chunk-Z6UN4ZPW.mjs → chunk-BVHVYC7Q.mjs} +4 -4
- package/dist/{chunk-WJENEFDG.js → chunk-CAFVLHDV.js} +3 -3
- package/dist/{chunk-WJENEFDG.js.map → chunk-CAFVLHDV.js.map} +1 -1
- package/dist/{chunk-BVUPM6EY.js → chunk-CAQ2Q3ON.js} +2 -2
- package/dist/chunk-CAQ2Q3ON.js.map +1 -0
- package/dist/{chunk-QMPOGLAR.js → chunk-CIASXVWX.js} +3 -3
- package/dist/{chunk-QMPOGLAR.js.map → chunk-CIASXVWX.js.map} +1 -1
- package/dist/{chunk-APKEYUJ3.js → chunk-CIQLC2ZZ.js} +3 -3
- package/dist/{chunk-APKEYUJ3.js.map → chunk-CIQLC2ZZ.js.map} +1 -1
- package/dist/{chunk-36XPNLGQ.js → chunk-CW3FCWR4.js} +16 -16
- package/dist/{chunk-36XPNLGQ.js.map → chunk-CW3FCWR4.js.map} +1 -1
- package/dist/{chunk-J5VEAJN5.mjs → chunk-CWVUGZNK.mjs} +2 -2
- package/dist/{chunk-BCMDP6GK.mjs → chunk-DV3N2K3Y.mjs} +4 -4
- package/dist/{chunk-PP7SEERV.mjs → chunk-DXYSEBK3.mjs} +9 -3
- package/dist/chunk-DXYSEBK3.mjs.map +1 -0
- package/dist/{chunk-754M6HQ5.js → chunk-EJN4OP3D.js} +5 -5
- package/dist/{chunk-754M6HQ5.js.map → chunk-EJN4OP3D.js.map} +1 -1
- package/dist/{chunk-3IM2SBPO.js → chunk-F5K3HMKT.js} +4 -4
- package/dist/{chunk-3IM2SBPO.js.map → chunk-F5K3HMKT.js.map} +1 -1
- package/dist/{chunk-WMDSGVZ7.mjs → chunk-GCGSAS2Z.mjs} +48 -63
- package/dist/chunk-GCGSAS2Z.mjs.map +1 -0
- package/dist/{chunk-EXHGEJ4P.mjs → chunk-GKARHTZ2.mjs} +2 -2
- package/dist/{chunk-2VDCEEXB.js → chunk-GNKFBVW4.js} +3 -3
- package/dist/{chunk-2VDCEEXB.js.map → chunk-GNKFBVW4.js.map} +1 -1
- package/dist/{chunk-CDLOMECB.js → chunk-GTBPMNTJ.js} +3 -3
- package/dist/{chunk-CDLOMECB.js.map → chunk-GTBPMNTJ.js.map} +1 -1
- package/dist/{chunk-YQKAASWM.js → chunk-GXXDSBN2.js} +10 -4
- package/dist/chunk-GXXDSBN2.js.map +1 -0
- package/dist/{chunk-FASVPU6N.js → chunk-H3F4OCXC.js} +10 -10
- package/dist/{chunk-FASVPU6N.js.map → chunk-H3F4OCXC.js.map} +1 -1
- package/dist/{chunk-JCFN6KRS.mjs → chunk-HGWDB73S.mjs} +2 -2
- package/dist/{chunk-JXXMO7PY.mjs → chunk-HN5RW4C3.mjs} +3 -3
- package/dist/{chunk-I2A4EQPH.mjs → chunk-HQ2GXFK5.mjs} +6 -6
- package/dist/{chunk-BIPC6F6D.js → chunk-IDJ37MOU.js} +115 -115
- package/dist/{chunk-BIPC6F6D.js.map → chunk-IDJ37MOU.js.map} +1 -1
- package/dist/{chunk-TBJLNIOY.mjs → chunk-JBLYYTHN.mjs} +2 -2
- package/dist/{chunk-G4NK66PM.js → chunk-JTWXQ267.js} +24 -18
- package/dist/chunk-JTWXQ267.js.map +1 -0
- package/dist/{chunk-V6IC74KH.js → chunk-JXGLDRDC.js} +34 -34
- package/dist/{chunk-V6IC74KH.js.map → chunk-JXGLDRDC.js.map} +1 -1
- package/dist/{chunk-DDFIRQI5.mjs → chunk-K3E5VEBF.mjs} +56 -34
- package/dist/chunk-K3E5VEBF.mjs.map +1 -0
- package/dist/{chunk-MSBEWG26.js → chunk-K5O2IOI4.js} +8 -8
- package/dist/{chunk-MSBEWG26.js.map → chunk-K5O2IOI4.js.map} +1 -1
- package/dist/{chunk-SB5YGKXR.js → chunk-KBMLGQ5J.js} +49 -64
- package/dist/chunk-KBMLGQ5J.js.map +1 -0
- package/dist/chunk-KCZEKV7M.mjs +81 -0
- package/dist/chunk-KCZEKV7M.mjs.map +1 -0
- package/dist/{chunk-GRSN76BV.mjs → chunk-KH2R4STU.mjs} +3 -3
- package/dist/{chunk-7DQM34JT.js → chunk-KHYSXYPO.js} +23 -23
- package/dist/{chunk-7DQM34JT.js.map → chunk-KHYSXYPO.js.map} +1 -1
- package/dist/{chunk-V2YHMPRR.js → chunk-KJHBQQYJ.js} +5 -5
- package/dist/{chunk-V2YHMPRR.js.map → chunk-KJHBQQYJ.js.map} +1 -1
- package/dist/{chunk-ZF5NMQJZ.mjs → chunk-KUKLN3CK.mjs} +4 -4
- package/dist/{chunk-6AYB7CZH.js → chunk-KZQVGSR2.js} +2 -2
- package/dist/{chunk-6AYB7CZH.js.map → chunk-KZQVGSR2.js.map} +1 -1
- package/dist/{chunk-UAKRBNW2.mjs → chunk-L4T76OKM.mjs} +2 -2
- package/dist/{chunk-AUCXJ7LG.js → chunk-M3LQH7Y7.js} +3 -3
- package/dist/{chunk-AUCXJ7LG.js.map → chunk-M3LQH7Y7.js.map} +1 -1
- package/dist/{chunk-FQNJJH7F.js → chunk-MCXRKD56.js} +13 -13
- package/dist/{chunk-FQNJJH7F.js.map → chunk-MCXRKD56.js.map} +1 -1
- package/dist/{chunk-PY5WCBKN.mjs → chunk-MERKXOSI.mjs} +4 -4
- package/dist/{chunk-D3YDZF2G.js → chunk-N472HQPZ.js} +3 -3
- package/dist/{chunk-D3YDZF2G.js.map → chunk-N472HQPZ.js.map} +1 -1
- package/dist/{chunk-UPMYCGXR.js → chunk-N4K4X34Z.js} +4 -4
- package/dist/{chunk-UPMYCGXR.js.map → chunk-N4K4X34Z.js.map} +1 -1
- package/dist/{chunk-RU43UMEH.js → chunk-NFCNAFUK.js} +3 -3
- package/dist/{chunk-RU43UMEH.js.map → chunk-NFCNAFUK.js.map} +1 -1
- package/dist/{chunk-APCB5HWT.mjs → chunk-NLSDZJRR.mjs} +3 -3
- package/dist/{chunk-EWYIXCNF.mjs → chunk-NLUTNCLH.mjs} +5 -5
- package/dist/{chunk-OP4Q2KDS.mjs → chunk-NMKKMY5P.mjs} +2 -2
- package/dist/{chunk-WDPFO4II.mjs → chunk-NMPK336K.mjs} +2 -2
- package/dist/{chunk-X6BZ5IEV.mjs → chunk-NU7O6YKR.mjs} +2 -2
- package/dist/{chunk-ZABDSRX3.js → chunk-NUZ5K3CB.js} +4 -4
- package/dist/{chunk-ZABDSRX3.js.map → chunk-NUZ5K3CB.js.map} +1 -1
- package/dist/{chunk-N5ZGFYLA.js → chunk-NZX3BA62.js} +3 -3
- package/dist/{chunk-N5ZGFYLA.js.map → chunk-NZX3BA62.js.map} +1 -1
- package/dist/{chunk-2TLSEAL2.js → chunk-O4TRDWXJ.js} +3 -3
- package/dist/{chunk-2TLSEAL2.js.map → chunk-O4TRDWXJ.js.map} +1 -1
- package/dist/{chunk-CBI5F4ME.mjs → chunk-OMSSA4NO.mjs} +2 -2
- package/dist/{chunk-YGEEWLGJ.js → chunk-PKKXCX5B.js} +3 -3
- package/dist/{chunk-YGEEWLGJ.js.map → chunk-PKKXCX5B.js.map} +1 -1
- package/dist/{chunk-GYHD6QEA.mjs → chunk-PQGQ3EH5.mjs} +2 -2
- package/dist/{chunk-3LN5GQAC.js → chunk-Q6UIUYNF.js} +6 -8
- package/dist/chunk-Q6UIUYNF.js.map +1 -0
- package/dist/chunk-QLWBGM7H.js +81 -0
- package/dist/chunk-QLWBGM7H.js.map +1 -0
- package/dist/{chunk-IF5GW6ID.js → chunk-QZ5DCDZ2.js} +6 -6
- package/dist/{chunk-IF5GW6ID.js.map → chunk-QZ5DCDZ2.js.map} +1 -1
- package/dist/{chunk-LSUCENYD.mjs → chunk-RBKGHUSR.mjs} +2 -2
- package/dist/{chunk-PIZTI5SJ.js → chunk-RMCZ7C2C.js} +3 -3
- package/dist/{chunk-PIZTI5SJ.js.map → chunk-RMCZ7C2C.js.map} +1 -1
- package/dist/{chunk-LGQNMQOT.mjs → chunk-ROS5XGTQ.mjs} +2 -2
- package/dist/chunk-ROS5XGTQ.mjs.map +1 -0
- package/dist/{chunk-TEUERRDG.mjs → chunk-RV45XVXE.mjs} +2 -2
- package/dist/{chunk-FW6ZI2MC.mjs → chunk-UAFSWXN5.mjs} +2 -2
- package/dist/{chunk-QUGKQYAJ.mjs → chunk-ULVFVLFE.mjs} +4 -6
- package/dist/{chunk-QUGKQYAJ.mjs.map → chunk-ULVFVLFE.mjs.map} +1 -1
- package/dist/{chunk-ANLL2EIX.mjs → chunk-UWCGTH3U.mjs} +2 -2
- package/dist/{chunk-6R2LEEEP.mjs → chunk-UZF2D2GG.mjs} +2 -2
- package/dist/{chunk-62L3PMYE.mjs → chunk-V3OVMHZD.mjs} +2 -2
- package/dist/{chunk-UDITT3N3.mjs → chunk-V6ELDZ2B.mjs} +2 -2
- package/dist/{chunk-G7NUCBD3.js → chunk-VDPV24JJ.js} +3 -3
- package/dist/{chunk-G7NUCBD3.js.map → chunk-VDPV24JJ.js.map} +1 -1
- package/dist/{chunk-IJQOTS5O.mjs → chunk-VJUS3GC4.mjs} +2 -2
- package/dist/{chunk-ZUPP7R2K.js → chunk-W7BBVDBI.js} +17 -3
- package/dist/chunk-W7BBVDBI.js.map +1 -0
- package/dist/{chunk-EHQDQ5RV.js → chunk-XARMPXRN.js} +10 -12
- package/dist/chunk-XARMPXRN.js.map +1 -0
- package/dist/{chunk-MQEAEU6G.mjs → chunk-XGMRYNER.mjs} +2 -2
- package/dist/{chunk-MMQMVVD6.mjs → chunk-XLTD5J74.mjs} +3 -3
- package/dist/{chunk-L3ST5GUJ.js → chunk-XQUK7R7L.js} +17 -17
- package/dist/{chunk-L3ST5GUJ.js.map → chunk-XQUK7R7L.js.map} +1 -1
- package/dist/{chunk-OMBUOOMD.mjs → chunk-XYT542WD.mjs} +2 -2
- package/dist/{chunk-IZNABVXE.js → chunk-Y4IGIWP4.js} +3 -3
- package/dist/{chunk-IZNABVXE.js.map → chunk-Y4IGIWP4.js.map} +1 -1
- package/dist/{chunk-COWOGT7U.mjs → chunk-YZWFDSTC.mjs} +2 -2
- package/dist/{chunk-M2FN7CNS.mjs → chunk-Z4L2DWMX.mjs} +17 -3
- package/dist/chunk-Z4L2DWMX.mjs.map +1 -0
- package/dist/chunk-Z75J66LL.mjs +149 -0
- package/dist/chunk-Z75J66LL.mjs.map +1 -0
- package/dist/{chunk-CWIJ7RXR.js → chunk-ZHUFICW3.js} +3 -3
- package/dist/{chunk-CWIJ7RXR.js.map → chunk-ZHUFICW3.js.map} +1 -1
- package/dist/{chunk-Q5XGBTOY.js → chunk-ZNITTMV5.js} +3 -3
- package/dist/{chunk-Q5XGBTOY.js.map → chunk-ZNITTMV5.js.map} +1 -1
- package/dist/components/EnemMoment/HeaderFilters.d.ts +12 -1
- package/dist/components/EnemMoment/HeaderFilters.d.ts.map +1 -1
- package/dist/components/EnemMoment/types.d.ts +10 -1
- package/dist/components/EnemMoment/types.d.ts.map +1 -1
- package/dist/hooks/useAppContent/index.js +48 -49
- package/dist/hooks/useAppContent/index.js.map +1 -1
- package/dist/hooks/useAppContent/index.mjs +47 -48
- package/dist/hooks/useResizableColumns.d.ts +20 -9
- package/dist/hooks/useResizableColumns.d.ts.map +1 -1
- package/dist/hooks/useScreen/index.js +2 -2
- package/dist/hooks/useScreen/index.mjs +1 -1
- package/dist/index.js +48 -49
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +47 -48
- package/dist/store/layoutPreferencesStore.d.ts +12 -24
- package/dist/store/layoutPreferencesStore.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/RichEditorCore-62NIM7PC.js.map +0 -1
- package/dist/chunk-2EDRPEYL.mjs +0 -157
- package/dist/chunk-2EDRPEYL.mjs.map +0 -1
- package/dist/chunk-2VODGC76.js.map +0 -1
- package/dist/chunk-3LN5GQAC.js.map +0 -1
- package/dist/chunk-BVUPM6EY.js.map +0 -1
- package/dist/chunk-CH6BWMSQ.js +0 -17
- package/dist/chunk-CH6BWMSQ.js.map +0 -1
- package/dist/chunk-DDFIRQI5.mjs.map +0 -1
- package/dist/chunk-EHQDQ5RV.js.map +0 -1
- package/dist/chunk-G4NK66PM.js.map +0 -1
- package/dist/chunk-HIIOADV4.js.map +0 -1
- package/dist/chunk-JSH3BQZ7.mjs +0 -17
- package/dist/chunk-JSH3BQZ7.mjs.map +0 -1
- package/dist/chunk-LGQNMQOT.mjs.map +0 -1
- package/dist/chunk-LMDAF6LA.mjs +0 -51
- package/dist/chunk-LMDAF6LA.mjs.map +0 -1
- package/dist/chunk-M2FN7CNS.mjs.map +0 -1
- package/dist/chunk-MUCV5ZWZ.mjs.map +0 -1
- package/dist/chunk-PJOBOBNJ.js +0 -51
- package/dist/chunk-PJOBOBNJ.js.map +0 -1
- package/dist/chunk-PP7SEERV.mjs.map +0 -1
- package/dist/chunk-SB5YGKXR.js.map +0 -1
- package/dist/chunk-WMDSGVZ7.mjs.map +0 -1
- package/dist/chunk-YQKAASWM.js.map +0 -1
- package/dist/chunk-ZUPP7R2K.js.map +0 -1
- /package/dist/{chunk-EVBQNVQW.mjs.map → chunk-233FSBU7.mjs.map} +0 -0
- /package/dist/{chunk-MADPP2PW.mjs.map → chunk-3ZSI2KI5.mjs.map} +0 -0
- /package/dist/{chunk-2W7PTFXS.mjs.map → chunk-42XKJV56.mjs.map} +0 -0
- /package/dist/{chunk-M6TC6XRQ.mjs.map → chunk-55ESXRRM.mjs.map} +0 -0
- /package/dist/{chunk-4MURAKPC.mjs.map → chunk-5DJVOLVX.mjs.map} +0 -0
- /package/dist/{chunk-YVT7CT3G.mjs.map → chunk-5P6CBDZ6.mjs.map} +0 -0
- /package/dist/{chunk-SFEU7BMY.mjs.map → chunk-5SFNRXZB.mjs.map} +0 -0
- /package/dist/{chunk-KYY4U5SN.mjs.map → chunk-6Z5W5J44.mjs.map} +0 -0
- /package/dist/{chunk-GVYGPJH3.mjs.map → chunk-AU5M2766.mjs.map} +0 -0
- /package/dist/{chunk-Z6UN4ZPW.mjs.map → chunk-BVHVYC7Q.mjs.map} +0 -0
- /package/dist/{chunk-J5VEAJN5.mjs.map → chunk-CWVUGZNK.mjs.map} +0 -0
- /package/dist/{chunk-BCMDP6GK.mjs.map → chunk-DV3N2K3Y.mjs.map} +0 -0
- /package/dist/{chunk-EXHGEJ4P.mjs.map → chunk-GKARHTZ2.mjs.map} +0 -0
- /package/dist/{chunk-JCFN6KRS.mjs.map → chunk-HGWDB73S.mjs.map} +0 -0
- /package/dist/{chunk-JXXMO7PY.mjs.map → chunk-HN5RW4C3.mjs.map} +0 -0
- /package/dist/{chunk-I2A4EQPH.mjs.map → chunk-HQ2GXFK5.mjs.map} +0 -0
- /package/dist/{chunk-TBJLNIOY.mjs.map → chunk-JBLYYTHN.mjs.map} +0 -0
- /package/dist/{chunk-GRSN76BV.mjs.map → chunk-KH2R4STU.mjs.map} +0 -0
- /package/dist/{chunk-ZF5NMQJZ.mjs.map → chunk-KUKLN3CK.mjs.map} +0 -0
- /package/dist/{chunk-UAKRBNW2.mjs.map → chunk-L4T76OKM.mjs.map} +0 -0
- /package/dist/{chunk-PY5WCBKN.mjs.map → chunk-MERKXOSI.mjs.map} +0 -0
- /package/dist/{chunk-APCB5HWT.mjs.map → chunk-NLSDZJRR.mjs.map} +0 -0
- /package/dist/{chunk-EWYIXCNF.mjs.map → chunk-NLUTNCLH.mjs.map} +0 -0
- /package/dist/{chunk-OP4Q2KDS.mjs.map → chunk-NMKKMY5P.mjs.map} +0 -0
- /package/dist/{chunk-WDPFO4II.mjs.map → chunk-NMPK336K.mjs.map} +0 -0
- /package/dist/{chunk-X6BZ5IEV.mjs.map → chunk-NU7O6YKR.mjs.map} +0 -0
- /package/dist/{chunk-CBI5F4ME.mjs.map → chunk-OMSSA4NO.mjs.map} +0 -0
- /package/dist/{chunk-GYHD6QEA.mjs.map → chunk-PQGQ3EH5.mjs.map} +0 -0
- /package/dist/{chunk-LSUCENYD.mjs.map → chunk-RBKGHUSR.mjs.map} +0 -0
- /package/dist/{chunk-TEUERRDG.mjs.map → chunk-RV45XVXE.mjs.map} +0 -0
- /package/dist/{chunk-FW6ZI2MC.mjs.map → chunk-UAFSWXN5.mjs.map} +0 -0
- /package/dist/{chunk-ANLL2EIX.mjs.map → chunk-UWCGTH3U.mjs.map} +0 -0
- /package/dist/{chunk-6R2LEEEP.mjs.map → chunk-UZF2D2GG.mjs.map} +0 -0
- /package/dist/{chunk-62L3PMYE.mjs.map → chunk-V3OVMHZD.mjs.map} +0 -0
- /package/dist/{chunk-UDITT3N3.mjs.map → chunk-V6ELDZ2B.mjs.map} +0 -0
- /package/dist/{chunk-IJQOTS5O.mjs.map → chunk-VJUS3GC4.mjs.map} +0 -0
- /package/dist/{chunk-MQEAEU6G.mjs.map → chunk-XGMRYNER.mjs.map} +0 -0
- /package/dist/{chunk-MMQMVVD6.mjs.map → chunk-XLTD5J74.mjs.map} +0 -0
- /package/dist/{chunk-OMBUOOMD.mjs.map → chunk-XYT542WD.mjs.map} +0 -0
- /package/dist/{chunk-COWOGT7U.mjs.map → chunk-YZWFDSTC.mjs.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-IZNABVXE.js","../src/components/SimulatedPerformance/useSimulatedPerformance.tsx","../src/components/GeneralOverviewSection/GeneralOverviewSection.tsx","../src/components/GeneralOverviewSection/useGeneralOverview.ts","../src/components/SimulatedStudentsOverview/useSimulatedOverview.ts","../src/components/SimulatedStudentDetailsModal/types.ts","../src/components/SimulatedStudentsOverview/utils.ts","../src/components/SimulatedStudentsOverview/useAggregatedOverview.ts","../src/components/SimulatedContentsPerformance/useSimulatedContents.ts","../src/components/AreaKnowledgeSelector/AreaKnowledgeSelector.tsx","../src/components/AreaKnowledgeSelector/types.ts","../src/components/SimulatedStudentDetailsModal/SimulatedStudentDetailsModal.tsx","../src/components/SimulatedStudentDetailsModal/useSimulatedStudentDetails.ts","../src/components/SimulatedStudentDetailsModal/utils.ts","../src/components/SimulatedStudentDetailsModal/exportSheets.ts","../src/utils/profileAggregation.ts","../src/components/SimulatedPerformance/types.ts"],"names":["useCallback","PerformanceBadgeAction","ReportSimulationType","endpoint","useState","initialState","buildEndpoint","jsx","jsxs","SimulatedViewTab","useMemo","useRef"],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-IZNABVXE.js","sourcesContent":[null,"import { useEffect, useCallback, useState, useMemo, useRef } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport Badge from '../Badge/Badge';\nimport { useGeneralOverview } from '../GeneralOverviewSection';\nimport {\n useSimulatedOverview,\n useAggregatedOverview,\n} from '../SimulatedStudentsOverview';\nimport { useSimulatedContents } from '../SimulatedContentsPerformance';\nimport { ESSAY_AREA_ID } from '../AreaKnowledgeSelector';\nimport { SIMULATED_PERFORMANCE_TAG_CONFIG } from '../SimulatedStudentDetailsModal';\nimport { formatScore } from '../../utils/utils';\nimport { getAggregationTypeByProfile } from '../../utils/profileAggregation';\nimport type { ColumnConfig } from '../TableProvider';\nimport type { StudentsHighlightPeriod } from '../../hooks/useStudentsHighlight';\nimport { ScoreType } from '../../types/common';\nimport type { SimulatedFilters } from '../SimulatedFilters/types';\nimport type { SimulatedStudentItem } from '../SimulatedStudentsOverview/types';\nimport type { SimulatedContentItem } from '../SimulatedContentsPerformance/types';\nimport {\n SimulatedViewTab,\n type UseSimulatedPerformanceOptions,\n type UseSimulatedPerformanceReturn,\n} from './types';\n\n/**\n * Performance tag cell renderer for simulated exams\n */\nfunction SimulatedPerformanceTagCell({\n row,\n}: {\n readonly row: SimulatedStudentItem;\n}) {\n const tagConfig = SIMULATED_PERFORMANCE_TAG_CONFIG[row.performance];\n\n return (\n <Badge variant=\"solid\" action={tagConfig.variant} size=\"small\">\n {tagConfig.label}\n </Badge>\n );\n}\n\n/**\n * Performance cell renderer for contents/skills with progress bar\n */\nfunction ContentPerformanceCell({\n percentage,\n}: {\n readonly percentage: number;\n}) {\n const getColorClass = (): string => {\n if (percentage >= 70) {\n return 'bg-success-500';\n }\n\n if (percentage >= 50) {\n return 'bg-warning-500';\n }\n\n return 'bg-error-500';\n };\n\n const colorClass = getColorClass();\n\n return (\n <div className=\"flex items-center justify-center gap-2\">\n <span className=\"text-sm text-text-950 min-w-[40px]\">\n {Math.round(percentage)}%\n </span>\n <div className=\"w-16 h-2 bg-background-100 rounded-full overflow-hidden\">\n <div\n className={`h-full rounded-full ${colorClass}`}\n style={{ width: `${percentage}%` }}\n />\n </div>\n </div>\n );\n}\n\n/**\n * Shared defaults for centered columns\n */\nconst centeredColumn: Partial<ColumnConfig<SimulatedStudentItem>> = {\n sortable: true,\n className: 'py-3 px-4 text-center',\n align: 'center',\n};\n\n/**\n * Shared defaults for centered columns (contents table)\n */\nconst centeredContentsColumn: Partial<ColumnConfig<SimulatedContentItem>> = {\n sortable: true,\n className: 'py-3 px-4 text-center',\n align: 'center',\n};\n\n/**\n * Base columns for simulated exams view (without render functions that depend on scoreType)\n */\nconst simulatedTableColumnsBase: Omit<\n ColumnConfig<SimulatedStudentItem>,\n 'render'\n>[] = [\n {\n key: 'name',\n label: 'Nome',\n sortable: true,\n className: 'py-3 px-4 text-start',\n },\n {\n key: 'school',\n label: 'Escola',\n sortable: false,\n className: 'py-3 px-4 text-start',\n },\n {\n ...centeredColumn,\n key: 'schoolYear',\n label: 'Ano',\n width: '80px',\n sortable: false,\n },\n {\n ...centeredColumn,\n key: 'class',\n label: 'Turma',\n width: '80px',\n sortable: false,\n },\n {\n ...centeredColumn,\n key: 'average',\n label: 'Média',\n width: '100px',\n },\n {\n ...centeredColumn,\n key: 'performance',\n label: 'Desempenho',\n width: '160px',\n },\n];\n\n/**\n * Table columns for simulated contents/skills view\n */\nconst contentsTableColumns: ColumnConfig<SimulatedContentItem>[] = [\n {\n key: 'contentName',\n label: 'Habilidade',\n sortable: true,\n className: 'py-3 px-4 text-start',\n },\n {\n key: 'subject',\n label: 'Componente',\n sortable: false,\n className: 'py-3 px-4 text-start',\n render: (_value: unknown, row: SimulatedContentItem) => row.subject.name,\n },\n {\n ...centeredContentsColumn,\n key: 'simulatedExamsCount',\n label: 'Simulados',\n width: '100px',\n },\n {\n ...centeredContentsColumn,\n key: 'questionsCount',\n label: 'Questões',\n width: '100px',\n },\n {\n ...centeredContentsColumn,\n key: 'studentsCount',\n label: 'Estudantes',\n width: '100px',\n },\n {\n ...centeredContentsColumn,\n key: 'performance',\n label: 'Desempenho',\n width: '160px',\n render: (_value: unknown, row: SimulatedContentItem) => (\n <ContentPerformanceCell percentage={row.performance.correctPercentage} />\n ),\n },\n];\n\n/**\n * Hook de orquestração para a funcionalidade de Simulados.\n * Gerencia estados, query params, carregamento de dados e handlers.\n */\nexport function useSimulatedPerformance({\n api,\n profileName,\n enabled = true,\n}: UseSimulatedPerformanceOptions): UseSimulatedPerformanceReturn {\n const [searchParams, setSearchParams] = useSearchParams();\n\n // === Aggregation Type based on Profile ===\n const aggregationType = useMemo(\n () => getAggregationTypeByProfile(profileName),\n [profileName]\n );\n\n // === Query Params ===\n const period = useMemo((): StudentsHighlightPeriod => {\n const periodParam = searchParams.get('period');\n if (periodParam === '7_DAYS') return '7_DAYS';\n if (periodParam === '1_MONTH') return '1_MONTH';\n if (periodParam === '3_MONTHS') return '3_MONTHS';\n if (periodParam === '6_MONTHS') return '6_MONTHS';\n if (periodParam === '1_YEAR') return '1_YEAR';\n return '1_MONTH';\n }, [searchParams]);\n\n const scoreType = useMemo((): ScoreType => {\n const scoreTypeParam = searchParams.get('scoreType');\n if (scoreTypeParam === 'tri') return ScoreType.TRI;\n return ScoreType.PERCENTAGE;\n }, [searchParams]);\n\n // === Estados de Seleção ===\n const [selectedAreaKnowledgeId, setSelectedAreaKnowledgeId] = useState<\n string | null\n >(null);\n const [selectedAreaRelatedIds, setSelectedAreaRelatedIds] = useState<\n string[]\n >([]);\n const [selectedSubjectId, setSelectedSubjectId] = useState<string | null>(\n null\n );\n const [simulatedViewTab, setSimulatedViewTab] = useState<SimulatedViewTab>(\n SimulatedViewTab.STUDENTS\n );\n\n // === Estados de Modais ===\n const [isStudentModalOpen, setIsStudentModalOpen] = useState(false);\n const [selectedStudent, setSelectedStudent] = useState<{\n userInstitutionId: string;\n name: string;\n } | null>(null);\n\n const [isContentModalOpen, setIsContentModalOpen] = useState(false);\n const [selectedContent, setSelectedContent] = useState<{\n contentId: string;\n contentName: string;\n } | null>(null);\n\n const [isFiltersModalOpen, setIsFiltersModalOpen] = useState(false);\n\n // === Filtros ===\n const [filters, setFilters] = useState<SimulatedFilters>({\n schoolIds: [],\n schoolYearIds: [],\n classIds: [],\n studentsIds: [],\n });\n\n // === Refs para evitar closure issues ===\n const selectedAreaKnowledgeIdRef = useRef<string | null>(\n selectedAreaKnowledgeId\n );\n const selectedSubjectIdRef = useRef<string | null>(selectedSubjectId);\n const filtersRef = useRef<SimulatedFilters>(filters);\n const simulatedViewTabRef = useRef<SimulatedViewTab>(simulatedViewTab);\n const scoreTypeRef = useRef<ScoreType>(scoreType);\n const periodRef = useRef<StudentsHighlightPeriod>(period);\n const aggregationTypeRef = useRef(aggregationType);\n\n // Keep refs in sync\n useEffect(() => {\n selectedAreaKnowledgeIdRef.current = selectedAreaKnowledgeId;\n }, [selectedAreaKnowledgeId]);\n\n useEffect(() => {\n selectedSubjectIdRef.current = selectedSubjectId;\n }, [selectedSubjectId]);\n\n useEffect(() => {\n filtersRef.current = filters;\n }, [filters]);\n\n useEffect(() => {\n simulatedViewTabRef.current = simulatedViewTab;\n }, [simulatedViewTab]);\n\n useEffect(() => {\n scoreTypeRef.current = scoreType;\n }, [scoreType]);\n\n useEffect(() => {\n periodRef.current = period;\n }, [period]);\n\n useEffect(() => {\n aggregationTypeRef.current = aggregationType;\n }, [aggregationType]);\n\n // === Hooks de API ===\n const {\n data: generalOverviewData,\n loading: generalOverviewLoading,\n error: generalOverviewError,\n fetchOverview: fetchGeneralOverview,\n } = useGeneralOverview(api);\n\n // Aggregated overview based on profile (classes/municipalities/students)\n const {\n data: aggregatedData,\n loading: aggregatedLoading,\n isRefreshing: aggregatedRefreshing,\n error: aggregatedError,\n fetchOverview: fetchAggregatedOverview,\n } = useAggregatedOverview(api);\n\n // Paginated students overview for table\n const {\n data: simulatedData,\n loading: simulatedLoading,\n isRefreshing: simulatedRefreshing,\n error: simulatedError,\n fetchOverview: fetchSimulatedOverview,\n } = useSimulatedOverview(api);\n\n const {\n data: contentsData,\n loading: contentsLoading,\n isRefreshing: contentsRefreshing,\n error: contentsError,\n fetchContents,\n } = useSimulatedContents(api);\n\n // === Valores derivados ===\n const isEssaySelected = selectedAreaKnowledgeId === ESSAY_AREA_ID;\n\n const activeFiltersCount = useMemo(() => {\n let count = 0;\n if (filters.schoolIds.length > 0) count++;\n if (filters.schoolYearIds.length > 0) count++;\n if (filters.classIds.length > 0) count++;\n if (filters.studentsIds.length > 0) count++;\n return count;\n }, [filters]);\n\n // Sync selectedAreaRelatedIds when generalOverviewData is refreshed\n useEffect(() => {\n if (!selectedAreaKnowledgeId || !generalOverviewData?.areas) {\n return;\n }\n const area = generalOverviewData.areas.find(\n (a) => a.id === selectedAreaKnowledgeId\n );\n const relatedIds = area?.relatedIds ?? [selectedAreaKnowledgeId];\n setSelectedAreaRelatedIds(relatedIds);\n }, [generalOverviewData, selectedAreaKnowledgeId]);\n\n // === Colunas das Tabelas (memoizadas) ===\n const studentsTableColumns = useMemo(\n (): ColumnConfig<SimulatedStudentItem>[] =>\n simulatedTableColumnsBase.map((col) => {\n if (col.key === 'average') {\n return {\n ...col,\n render: (_value: unknown, row: SimulatedStudentItem) =>\n formatScore(row.average, scoreType),\n };\n }\n if (col.key === 'performance') {\n return {\n ...col,\n render: (_value: unknown, row: SimulatedStudentItem) => (\n <SimulatedPerformanceTagCell row={row} />\n ),\n };\n }\n return col as ColumnConfig<SimulatedStudentItem>;\n }),\n [scoreType]\n );\n\n // === Funções de Carregamento ===\n const loadStudentsData = useCallback(\n async (\n areaKnowledgeId: string | null,\n subjectId: string | null,\n page = 1,\n refresh = false,\n periodOverride?: StudentsHighlightPeriod,\n scoreTypeOverride?: ScoreType\n ) => {\n const currentFilters = filtersRef.current;\n const currentPeriod = periodOverride ?? periodRef.current;\n const currentScoreType = scoreTypeOverride ?? scoreTypeRef.current;\n const isEssay = areaKnowledgeId === ESSAY_AREA_ID;\n\n const effectiveSubjectId =\n !isEssay && subjectId && subjectId !== 'all' ? subjectId : undefined;\n const effectiveAreaKnowledgeId =\n !isEssay && areaKnowledgeId && areaKnowledgeId !== 'all'\n ? areaKnowledgeId\n : undefined;\n\n await fetchSimulatedOverview(\n {\n simulationType: isEssay ? 'essays' : 'enem-1',\n period: currentPeriod,\n subjectId: effectiveSubjectId,\n areaKnowledgeId: effectiveAreaKnowledgeId,\n schoolIds: currentFilters.schoolIds,\n schoolYearIds: currentFilters.schoolYearIds,\n classIds: currentFilters.classIds,\n studentsIds: currentFilters.studentsIds,\n page,\n limit: 10,\n orderBy: 'name',\n order: 'asc',\n scoreType: currentScoreType,\n },\n refresh\n );\n },\n [fetchSimulatedOverview]\n );\n\n const loadSkillsData = useCallback(\n async (\n areaKnowledgeId: string | null,\n subjectId: string | null,\n periodOverride?: StudentsHighlightPeriod,\n scoreTypeOverride?: ScoreType\n ) => {\n // Essays não têm conteúdos/habilidades\n if (areaKnowledgeId === ESSAY_AREA_ID) {\n return;\n }\n\n const currentFilters = filtersRef.current;\n const currentPeriod = periodOverride ?? periodRef.current;\n const currentScoreType = scoreTypeOverride ?? scoreTypeRef.current;\n\n const effectiveSubjectId =\n subjectId && subjectId !== 'all' ? subjectId : undefined;\n const effectiveAreaKnowledgeId =\n areaKnowledgeId && areaKnowledgeId !== 'all'\n ? areaKnowledgeId\n : undefined;\n\n await fetchContents({\n simulationType: 'enem-1',\n period: currentPeriod,\n subjectId: effectiveSubjectId,\n areaKnowledgeId: effectiveAreaKnowledgeId,\n schoolIds: currentFilters.schoolIds,\n schoolYearIds: currentFilters.schoolYearIds,\n classIds: currentFilters.classIds,\n studentsIds: currentFilters.studentsIds,\n page: 1,\n limit: 10,\n orderBy: 'correctPercentage',\n order: 'asc',\n scoreType: currentScoreType,\n });\n },\n [fetchContents]\n );\n\n const loadGeneralOverviewData = useCallback(\n async (\n periodOverride?: StudentsHighlightPeriod,\n scoreTypeOverride?: ScoreType\n ) => {\n const currentFilters = filtersRef.current;\n const currentScoreType = scoreTypeOverride ?? scoreTypeRef.current;\n\n await fetchGeneralOverview({\n period: periodOverride ?? periodRef.current,\n schoolIds: currentFilters.schoolIds,\n schoolYearIds: currentFilters.schoolYearIds,\n classIds: currentFilters.classIds,\n studentsIds: currentFilters.studentsIds,\n scoreType: currentScoreType,\n });\n },\n [fetchGeneralOverview]\n );\n\n /**\n * Load aggregated overview data based on profile (classes/municipalities/students)\n */\n const loadAggregatedOverviewData = useCallback(\n async (\n areaKnowledgeId: string | null,\n subjectId: string | null,\n refresh = false,\n periodOverride?: StudentsHighlightPeriod,\n scoreTypeOverride?: ScoreType\n ) => {\n const currentFilters = filtersRef.current;\n const currentPeriod = periodOverride ?? periodRef.current;\n const currentScoreType = scoreTypeOverride ?? scoreTypeRef.current;\n const currentAggregationType = aggregationTypeRef.current;\n const isEssay = areaKnowledgeId === ESSAY_AREA_ID;\n\n const effectiveSubjectId =\n !isEssay && subjectId && subjectId !== 'all' ? subjectId : undefined;\n const effectiveAreaKnowledgeId =\n !isEssay && areaKnowledgeId && areaKnowledgeId !== 'all'\n ? areaKnowledgeId\n : undefined;\n\n await fetchAggregatedOverview(\n {\n aggregationType: currentAggregationType,\n simulationType: isEssay ? 'essays' : 'enem-1',\n period: currentPeriod,\n subjectId: effectiveSubjectId,\n areaKnowledgeId: effectiveAreaKnowledgeId,\n schoolIds: currentFilters.schoolIds,\n schoolYearIds: currentFilters.schoolYearIds,\n classIds: currentFilters.classIds,\n studentsIds: currentFilters.studentsIds,\n scoreType: currentScoreType,\n },\n refresh\n );\n },\n [fetchAggregatedOverview]\n );\n\n // === Handlers ===\n const handlePeriodChange = useCallback(\n (value: string) => {\n const newPeriod = value as StudentsHighlightPeriod;\n if (newPeriod === periodRef.current) {\n return;\n }\n const effectiveScoreType = scoreTypeRef.current;\n\n setSearchParams((prev) => {\n const newParams = new URLSearchParams(prev);\n if (newPeriod === '1_MONTH') {\n newParams.delete('period');\n } else {\n newParams.set('period', newPeriod);\n }\n if (effectiveScoreType === 'percentage') {\n newParams.delete('scoreType');\n } else {\n newParams.set('scoreType', effectiveScoreType);\n }\n return newParams;\n });\n\n // Reload aggregated overview\n loadAggregatedOverviewData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current,\n false,\n newPeriod,\n effectiveScoreType\n );\n\n // Reload data with new period\n if (simulatedViewTabRef.current === SimulatedViewTab.STUDENTS) {\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current,\n 1,\n false,\n newPeriod,\n effectiveScoreType\n );\n } else {\n loadSkillsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current,\n newPeriod,\n effectiveScoreType\n );\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current,\n 1,\n false,\n newPeriod,\n effectiveScoreType\n );\n }\n loadGeneralOverviewData(newPeriod, effectiveScoreType);\n },\n [\n setSearchParams,\n loadStudentsData,\n loadSkillsData,\n loadGeneralOverviewData,\n loadAggregatedOverviewData,\n ]\n );\n\n const handleScoreTypeChange = useCallback(\n (value: string) => {\n const newScoreType = value as ScoreType;\n if (newScoreType === scoreTypeRef.current) {\n return;\n }\n const effectivePeriod = periodRef.current;\n setSearchParams((prev) => {\n const newParams = new URLSearchParams(prev);\n if (effectivePeriod === '1_MONTH') {\n newParams.delete('period');\n } else {\n newParams.set('period', effectivePeriod);\n }\n if (newScoreType === 'percentage') {\n newParams.delete('scoreType');\n } else {\n newParams.set('scoreType', newScoreType);\n }\n return newParams;\n });\n },\n [setSearchParams]\n );\n\n const handleAreaKnowledgeChange = useCallback(\n (areaId: string | null) => {\n if (areaId === selectedAreaKnowledgeIdRef.current) {\n return;\n }\n\n // Find the area to get relatedIds (for merged areas with same name)\n const area = generalOverviewData?.areas.find((a) => a.id === areaId);\n const relatedIds = area?.relatedIds ?? (areaId ? [areaId] : []);\n\n setSelectedAreaKnowledgeId(areaId);\n setSelectedAreaRelatedIds(relatedIds);\n setSelectedSubjectId(null);\n setSimulatedViewTab(SimulatedViewTab.STUDENTS);\n\n loadAggregatedOverviewData(areaId, null);\n loadStudentsData(areaId, null);\n },\n [loadStudentsData, loadAggregatedOverviewData, generalOverviewData]\n );\n\n const handleSubjectChange = useCallback(\n (subjectId: string | null) => {\n const effectiveSubjectId =\n subjectId === 'all' || subjectId === null ? null : subjectId;\n if (effectiveSubjectId === selectedSubjectIdRef.current) {\n return;\n }\n\n setSelectedSubjectId(effectiveSubjectId);\n\n // Always reload aggregated overview\n loadAggregatedOverviewData(\n selectedAreaKnowledgeIdRef.current,\n effectiveSubjectId\n );\n\n if (simulatedViewTabRef.current === SimulatedViewTab.STUDENTS) {\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n effectiveSubjectId\n );\n } else {\n loadSkillsData(selectedAreaKnowledgeIdRef.current, effectiveSubjectId);\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n effectiveSubjectId\n );\n }\n },\n [loadStudentsData, loadSkillsData, loadAggregatedOverviewData]\n );\n\n const handleViewTabChange = useCallback(\n (value: string) => {\n const newTab = value as SimulatedViewTab;\n\n if (newTab === simulatedViewTabRef.current) {\n return;\n }\n\n setSimulatedViewTab(newTab);\n\n if (newTab === SimulatedViewTab.STUDENTS) {\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n } else {\n loadSkillsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n }\n },\n [loadStudentsData, loadSkillsData]\n );\n\n const handleFiltersApply = useCallback(\n (newFilters: SimulatedFilters) => {\n // Atualizar ref ANTES para que as funções de load usem os novos valores\n filtersRef.current = newFilters;\n setFilters(newFilters);\n\n // Reload aggregated overview\n loadAggregatedOverviewData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n\n if (simulatedViewTabRef.current === SimulatedViewTab.STUDENTS) {\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n } else {\n loadSkillsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n }\n loadGeneralOverviewData();\n },\n [\n loadStudentsData,\n loadSkillsData,\n loadGeneralOverviewData,\n loadAggregatedOverviewData,\n ]\n );\n\n const handleStudentsParamsChange = useCallback(\n (params: Record<string, unknown>) => {\n const newPage = params.page as number;\n const sortBy = params.sortBy as string;\n const sortOrder = params.sortOrder as string;\n\n const sortByMap: Record<string, string> = {\n name: 'name',\n school: 'school',\n schoolYear: 'schoolYear',\n class: 'class',\n average: 'average',\n performance: 'performance',\n };\n\n const orderBy = sortBy ? (sortByMap[sortBy] ?? 'name') : 'name';\n const order = sortOrder === 'desc' ? 'desc' : 'asc';\n const currentAreaKnowledgeId = selectedAreaKnowledgeIdRef.current;\n const currentSubjectId = selectedSubjectIdRef.current;\n const isEssay = currentAreaKnowledgeId === ESSAY_AREA_ID;\n\n const effectiveSubjectId =\n !isEssay && currentSubjectId && currentSubjectId !== 'all'\n ? currentSubjectId\n : undefined;\n const effectiveAreaKnowledgeId =\n !isEssay && currentAreaKnowledgeId && currentAreaKnowledgeId !== 'all'\n ? currentAreaKnowledgeId\n : undefined;\n\n fetchSimulatedOverview(\n {\n simulationType: isEssay ? 'essays' : 'enem-1',\n period: periodRef.current,\n subjectId: effectiveSubjectId,\n areaKnowledgeId: effectiveAreaKnowledgeId,\n schoolIds: filtersRef.current.schoolIds,\n schoolYearIds: filtersRef.current.schoolYearIds,\n classIds: filtersRef.current.classIds,\n studentsIds: filtersRef.current.studentsIds,\n page: newPage ?? 1,\n limit: 10,\n orderBy,\n order,\n scoreType: scoreTypeRef.current,\n },\n true\n );\n },\n [fetchSimulatedOverview]\n );\n\n const handleContentsParamsChange = useCallback(\n (params: Record<string, unknown>) => {\n if (selectedAreaKnowledgeIdRef.current === ESSAY_AREA_ID) {\n return;\n }\n\n const newPage = params.page as number;\n const newLimit = params.limit as number;\n const sortBy = params.sortBy as string;\n const sortOrder = params.sortOrder as string;\n\n const sortByMap: Record<string, string> = {\n contentName: 'contentName',\n simulatedExamsCount: 'simulatedExamsCount',\n questionsCount: 'questionsCount',\n studentsCount: 'studentsCount',\n performance: 'correctPercentage',\n };\n\n const orderBy = sortBy\n ? (sortByMap[sortBy] ?? 'correctPercentage')\n : 'correctPercentage';\n const order = sortOrder === 'desc' ? 'desc' : 'asc';\n\n const currentSubjectId = selectedSubjectIdRef.current;\n const currentAreaKnowledgeId = selectedAreaKnowledgeIdRef.current;\n const effectiveSubjectId =\n currentSubjectId && currentSubjectId !== 'all'\n ? currentSubjectId\n : undefined;\n const effectiveAreaKnowledgeId =\n currentAreaKnowledgeId && currentAreaKnowledgeId !== 'all'\n ? currentAreaKnowledgeId\n : undefined;\n\n fetchContents({\n simulationType: 'enem-1',\n period: periodRef.current,\n subjectId: effectiveSubjectId,\n areaKnowledgeId: effectiveAreaKnowledgeId,\n schoolIds: filtersRef.current.schoolIds,\n schoolYearIds: filtersRef.current.schoolYearIds,\n classIds: filtersRef.current.classIds,\n studentsIds: filtersRef.current.studentsIds,\n page: newPage ?? 1,\n limit: newLimit ?? 10,\n orderBy,\n order,\n scoreType: scoreTypeRef.current,\n });\n },\n [fetchContents]\n );\n\n const handleStudentRowClick = useCallback((row: SimulatedStudentItem) => {\n setSelectedStudent({\n userInstitutionId: row.userInstitutionId,\n name: row.name,\n });\n setIsStudentModalOpen(true);\n }, []);\n\n const handleContentRowClick = useCallback((row: SimulatedContentItem) => {\n setSelectedContent({\n contentId: row.contentId,\n contentName: row.contentName,\n });\n setIsContentModalOpen(true);\n }, []);\n\n // === Carregamento inicial ===\n const initialLoadRef = useRef(false);\n const hasInitialLoadCompleted = useRef(false);\n\n useEffect(() => {\n // Skip if hook is disabled (e.g., not on simulated report)\n if (!enabled) return;\n if (initialLoadRef.current) return;\n initialLoadRef.current = true;\n\n // Load aggregated overview (based on profile)\n loadAggregatedOverviewData(null, null);\n // Load paginated students for table\n loadStudentsData(null, null);\n loadGeneralOverviewData();\n\n // Set after microtask to prevent scoreType effect from firing on mount\n Promise.resolve().then(() => {\n hasInitialLoadCompleted.current = true;\n });\n }, [\n enabled,\n loadStudentsData,\n loadGeneralOverviewData,\n loadAggregatedOverviewData,\n ]);\n\n // === Recarregar quando scoreType muda ===\n useEffect(() => {\n // Skip if hook is disabled\n if (!enabled) return;\n if (!hasInitialLoadCompleted.current) return;\n\n // Always reload aggregated overview\n loadAggregatedOverviewData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n\n if (simulatedViewTabRef.current === SimulatedViewTab.STUDENTS) {\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n } else {\n loadSkillsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n }\n loadGeneralOverviewData();\n }, [\n enabled,\n scoreType,\n loadStudentsData,\n loadSkillsData,\n loadGeneralOverviewData,\n loadAggregatedOverviewData,\n ]);\n\n // === Recarregar quando aggregationType muda (perfil carregado após mount) ===\n const previousAggregationTypeRef = useRef(aggregationType);\n useEffect(() => {\n // Skip if hook is disabled\n if (!enabled) return;\n // Skip if aggregationType hasn't actually changed\n if (previousAggregationTypeRef.current === aggregationType) return;\n previousAggregationTypeRef.current = aggregationType;\n\n // Skip if initial load hasn't completed\n if (!hasInitialLoadCompleted.current) return;\n\n // Reload aggregated overview with new aggregation type\n loadAggregatedOverviewData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n }, [enabled, aggregationType, loadAggregatedOverviewData]);\n\n return {\n // Query Params\n period,\n scoreType,\n\n // Estados de Seleção\n selectedAreaKnowledgeId,\n selectedAreaRelatedIds,\n selectedSubjectId,\n simulatedViewTab,\n isEssaySelected,\n\n // Filtros\n filters,\n activeFiltersCount,\n\n // Aggregation Type\n aggregationType,\n\n // Dados da API\n generalOverview: {\n data: generalOverviewData,\n loading: generalOverviewLoading,\n error: generalOverviewError,\n },\n aggregatedOverview: {\n data: aggregatedData,\n loading: aggregatedLoading,\n isRefreshing: aggregatedRefreshing,\n error: aggregatedError,\n },\n studentsOverview: {\n data: simulatedData,\n loading: simulatedLoading,\n isRefreshing: simulatedRefreshing,\n error: simulatedError,\n },\n contentsPerformance: {\n data: contentsData,\n loading: contentsLoading,\n isRefreshing: contentsRefreshing,\n error: contentsError,\n },\n\n // Handlers\n handlePeriodChange,\n handleScoreTypeChange,\n handleAreaKnowledgeChange,\n handleSubjectChange,\n handleViewTabChange,\n handleFiltersApply,\n handleStudentsParamsChange,\n handleContentsParamsChange,\n handleStudentRowClick,\n handleContentRowClick,\n\n // Modais\n studentModal: {\n isOpen: isStudentModalOpen,\n student: selectedStudent,\n close: () => {\n setIsStudentModalOpen(false);\n setSelectedStudent(null);\n },\n },\n contentModal: {\n isOpen: isContentModalOpen,\n content: selectedContent,\n close: () => {\n setIsContentModalOpen(false);\n setSelectedContent(null);\n },\n },\n filtersModal: {\n isOpen: isFiltersModalOpen,\n open: () => setIsFiltersModalOpen(true),\n close: () => setIsFiltersModalOpen(false),\n },\n\n // Colunas das Tabelas\n studentsTableColumns,\n contentsTableColumns,\n };\n}\n","import Text from '../Text/Text';\nimport ProgressBar from '../ProgressBar/ProgressBar';\nimport { SkeletonRounded } from '../Skeleton/Skeleton';\nimport IconRender from '../IconRender/IconRender';\nimport { formatScore, hexToRgba } from '../../utils/utils';\nimport { ScoreType } from '../../types/common';\nimport {\n type GeneralOverviewSectionProps,\n type AreaKnowledgePerformance,\n type EssayPerformance,\n} from './types';\n\n/**\n * Area knowledge card component\n * Uses color and icon from API data\n */\nfunction AreaCard({\n area,\n scoreType = ScoreType.PERCENTAGE,\n}: {\n readonly area: AreaKnowledgePerformance;\n readonly scoreType?: ScoreType;\n}) {\n const bgColor = hexToRgba(area.color, 0.1);\n\n return (\n <div\n className=\"flex-1 min-w-0 flex flex-col items-center justify-center p-4 rounded-xl\"\n style={{ backgroundColor: bgColor }}\n >\n {/* Icon with rounded background */}\n <div\n className=\"w-8 h-8 flex items-center justify-center rounded-full mb-2\"\n style={{ backgroundColor: area.color }}\n >\n <IconRender iconName={area.icon} size={18} color=\"white\" />\n </div>\n\n {/* Full name (uppercase, smaller) */}\n <Text\n size=\"xs\"\n weight=\"semibold\"\n className=\"text-center uppercase text-text-700 mb-1 line-clamp-2\"\n >\n {area.name}\n </Text>\n\n {/* Score (percentage or TRI) */}\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n {formatScore(area.percentage, scoreType)}\n </Text>\n </div>\n );\n}\n\n/**\n * Essay card component\n * Displays essay performance using color and icon from API\n * Note: Essays always use percentage, TRI doesn't apply to essays\n */\nfunction EssayCard({ essay }: { readonly essay: EssayPerformance }) {\n const bgColor = hexToRgba(essay.color, 0.1);\n\n return (\n <div\n className=\"flex-1 min-w-0 flex flex-col items-center justify-center p-4 rounded-xl\"\n style={{ backgroundColor: bgColor }}\n >\n {/* Icon with rounded background */}\n <div\n className=\"w-8 h-8 flex items-center justify-center rounded-full mb-2\"\n style={{ backgroundColor: essay.color }}\n >\n <IconRender iconName={essay.icon} size={18} color=\"white\" />\n </div>\n\n {/* Name (uppercase, smaller) */}\n <Text\n size=\"xs\"\n weight=\"semibold\"\n className=\"text-center uppercase text-text-700 mb-1 line-clamp-2\"\n >\n {essay.name}\n </Text>\n\n {/* Percentage large - Essays always use percentage */}\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n {formatScore(essay.percentage, ScoreType.PERCENTAGE)}\n </Text>\n </div>\n );\n}\n\n/**\n * Skeleton loader for area cards\n */\nfunction AreaCardSkeleton() {\n return (\n <div className=\"flex-1 min-w-0 flex flex-col items-center justify-center p-4 rounded-xl bg-background-50\">\n <SkeletonRounded className=\"h-8 w-8 rounded-full mb-2\" />\n <SkeletonRounded className=\"h-3 w-16 mb-1\" />\n <SkeletonRounded className=\"h-6 w-12\" />\n </div>\n );\n}\n\n/**\n * GeneralOverviewSection - General overview section for simulated exams\n *\n * Displays overall proficiency percentage and performance by area of knowledge.\n * Uses color and icon directly from API data for each area.\n * This section is independent of areaKnowledgeId and subjectId filters -\n * it always shows consolidated data across all simulated exams.\n *\n * @example\n * ```tsx\n * const { data, loading, error, fetchOverview } = useGeneralOverview(api);\n *\n * <GeneralOverviewSection\n * data={data}\n * loading={loading}\n * error={error}\n * scoreType=\"tri\"\n * />\n * ```\n */\nexport function GeneralOverviewSection({\n data,\n loading = false,\n error = null,\n scoreType = ScoreType.PERCENTAGE,\n}: Readonly<GeneralOverviewSectionProps>) {\n if (error) {\n return (\n <div className=\"p-4 bg-error-50 border border-error-200 rounded-xl\">\n <Text size=\"sm\" className=\"text-error-700\">\n {error}\n </Text>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"space-y-4 p-5 bg-background border border-border-50 rounded-xl\">\n {/* Header */}\n <div className=\"space-y-1\">\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n Geral\n </Text>\n <SkeletonRounded className=\"h-4 w-3/4\" />\n </div>\n\n {/* Overall progress skeleton */}\n <div className=\"flex items-center gap-4\">\n <div className=\"flex-1\">\n <SkeletonRounded className=\"h-3 w-full rounded-full\" />\n </div>\n <SkeletonRounded className=\"h-6 w-12\" />\n </div>\n\n {/* Area cards skeleton */}\n <div className=\"flex gap-3\">\n {[1, 2, 3, 4, 5].map((i) => (\n <AreaCardSkeleton key={i} />\n ))}\n </div>\n </div>\n );\n }\n\n if (!data) {\n return null;\n }\n\n return (\n <div className=\"space-y-4 p-5 bg-background border border-border-50 rounded-xl\">\n {/* Header with description */}\n <div className=\"space-y-1\">\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n Geral\n </Text>\n <Text size=\"sm\" className=\"text-text-500\">\n Dados que mostram a proficiência de todos os simulados digitais já\n realizados em todas as áreas do conhecimento\n </Text>\n </div>\n\n {/* Overall progress bar */}\n <div className=\"flex items-center gap-4\">\n <div className=\"flex-1\">\n <ProgressBar\n value={\n scoreType === ScoreType.TRI\n ? data.overallPercentage / 10\n : data.overallPercentage\n }\n variant=\"green\"\n size=\"small\"\n />\n </div>\n <Text\n size=\"lg\"\n weight=\"bold\"\n className=\"text-text-950 min-w-[50px] text-right\"\n >\n {formatScore(data.overallPercentage, scoreType)}\n </Text>\n </div>\n\n {/* Area knowledge cards + Essay card - flex to fill 100% equally */}\n {(data.areas.length > 0 || data.essay) && (\n <div className=\"flex gap-3\">\n {data.areas.map((area) => (\n <AreaCard key={area.id} area={area} scoreType={scoreType} />\n ))}\n {data.essay && <EssayCard essay={data.essay} />}\n </div>\n )}\n </div>\n );\n}\n\nexport default GeneralOverviewSection;\n","import { useState, useCallback, useRef } from 'react';\nimport type { BaseApiClient } from '../../types/api';\nimport type {\n GeneralOverviewParams,\n GeneralOverviewApiResponse,\n UseGeneralOverviewState,\n UseGeneralOverviewReturn,\n} from './types';\n\n/**\n * Initial state for the hook\n */\nconst initialState: UseGeneralOverviewState = {\n data: null,\n loading: false,\n isRefreshing: false,\n error: null,\n};\n\n/**\n * Hook for fetching general overview data for simulated exams\n *\n * Receives the API client and calls the endpoint directly.\n * Endpoint: POST /performance/simulated/general-overview\n *\n * @param api - API client instance (axios-like)\n * @returns Hook state and methods\n *\n * @example\n * ```tsx\n * import api from '@/services/apiService';\n *\n * const { data, loading, error, fetchOverview } = useGeneralOverview(api);\n *\n * useEffect(() => {\n * fetchOverview({\n * period: '1_MONTH',\n * schoolIds: ['school-id'],\n * scoreType: 'tri',\n * });\n * }, []);\n * ```\n */\nexport function useGeneralOverview(\n api: BaseApiClient\n): UseGeneralOverviewReturn {\n const [state, setState] = useState<UseGeneralOverviewState>(initialState);\n const requestIdRef = useRef(0);\n\n /**\n * Fetch general overview data\n * @param params - Request parameters\n * @param refresh - If true, sets isRefreshing instead of loading\n */\n const fetchOverview = useCallback(\n async (params: GeneralOverviewParams, refresh = false) => {\n const currentRequestId = ++requestIdRef.current;\n\n // Set loading or refreshing state\n setState((prev) => ({\n ...prev,\n loading: !refresh,\n isRefreshing: refresh,\n error: null,\n }));\n\n try {\n // Build URL with optional scoreType query param\n let url = '/performance/simulated/general-overview';\n if (params.scoreType && params.scoreType !== 'percentage') {\n url += `?scoreType=${params.scoreType}`;\n }\n\n // Build request body (excludes scoreType and studentsIds)\n const body = {\n period: params.period,\n schoolIds: params.schoolIds || [],\n schoolYearIds: params.schoolYearIds || [],\n classIds: params.classIds || [],\n };\n\n const response = await api.post<GeneralOverviewApiResponse>(url, body);\n\n // Ignore stale responses\n if (currentRequestId !== requestIdRef.current) return;\n\n setState({\n data: response.data.data,\n loading: false,\n isRefreshing: false,\n error: null,\n });\n } catch (err) {\n // Ignore errors from stale requests\n if (currentRequestId !== requestIdRef.current) return;\n\n const errorMessage =\n err instanceof Error\n ? err.message\n : 'Erro ao carregar dados gerais de desempenho';\n\n console.error('Error fetching general overview:', err);\n\n setState((prev) => ({\n ...prev,\n loading: false,\n isRefreshing: false,\n error: errorMessage,\n }));\n }\n },\n [api]\n );\n\n /**\n * Reset state to initial values\n */\n const reset = useCallback(() => {\n // Invalidate any in-flight requests\n requestIdRef.current++;\n setState(initialState);\n }, []);\n\n return {\n ...state,\n fetchOverview,\n reset,\n };\n}\n\nexport default useGeneralOverview;\n","import { useState, useCallback, useRef } from 'react';\nimport type { BaseApiClient } from '../../types/api';\nimport type {\n SimulatedOverviewData,\n SimulatedOverviewParams,\n SimulatedOverviewApiResponse,\n UseSimulatedOverviewState,\n UseSimulatedOverviewReturn,\n} from './types';\nimport {\n simulationTypeToActivityFilters,\n SimulationType,\n} from '../SimulatedStudentDetailsModal/types';\nimport { getErrorMessage } from './utils';\n\n/**\n * Body type for POST endpoints\n */\ninterface SimulatedOverviewBody {\n period?: string;\n subjectId?: string;\n areaKnowledgeId?: string;\n schoolIds?: string[];\n schoolYearIds?: string[];\n classIds?: string[];\n studentsIds?: string[];\n page?: number;\n limit?: number;\n orderBy?: string;\n order?: 'asc' | 'desc';\n}\n\n/**\n * Build the API endpoint with activity filters and scoreType as query params\n */\nfunction buildEndpoint(\n simulationType: SimulationType,\n scoreType?: string\n): string {\n // Essays still use the legacy endpoint since they don't have activity filters\n if (simulationType === 'essays') {\n const params = new URLSearchParams();\n if (scoreType && scoreType !== 'percentage') {\n params.append('scoreType', scoreType);\n }\n const queryString = params.toString();\n const endpoint = '/performance/simulated/essays/students-overview';\n\n if (!queryString) {\n return endpoint;\n }\n\n return `${endpoint}?${queryString}`;\n }\n\n const activityFilters = simulationTypeToActivityFilters(simulationType);\n const params = new URLSearchParams();\n\n activityFilters.types?.forEach((t) => params.append('types', t));\n // Note: subtypes are not sent - the backend filters by areaKnowledgeId/subjectId in the body\n activityFilters.statuses?.forEach((s) => params.append('statuses', s));\n\n // Add scoreType if not default (percentage)\n if (scoreType && scoreType !== 'percentage') {\n params.append('scoreType', scoreType);\n }\n\n const queryString = params.toString();\n const endpoint = '/performance/simulated/activities/students-overview';\n\n if (!queryString) {\n return endpoint;\n }\n\n return `${endpoint}?${queryString}`;\n}\n\n/**\n * Initial state for the hook\n */\nconst initialState: UseSimulatedOverviewState = {\n data: null,\n loading: false,\n isRefreshing: false,\n error: null,\n};\n\n/**\n * Hook for fetching simulated exams overview data\n * Supports ENEM Prova 1, ENEM Prova 2, and Essays\n *\n * @param api - API client with post method\n *\n * @example\n * ```tsx\n * const { data, loading, error, fetchOverview } = useSimulatedOverview(api);\n *\n * fetchOverview({\n * simulationType: 'enem-1',\n * period: '1_MONTH',\n * subjectId: 'subject-uuid',\n * page: 1,\n * limit: 10,\n * });\n * ```\n */\nexport function useSimulatedOverview(\n api: BaseApiClient\n): UseSimulatedOverviewReturn {\n const [data, setData] = useState<SimulatedOverviewData | null>(\n initialState.data\n );\n const [loading, setLoading] = useState(initialState.loading);\n const [isRefreshing, setIsRefreshing] = useState(initialState.isRefreshing);\n const [error, setError] = useState<string | null>(initialState.error);\n\n // Track request ID to ignore stale responses\n const requestIdRef = useRef(0);\n\n /**\n * Convert params to POST body format\n */\n const paramsToBody = (\n params: SimulatedOverviewParams\n ): SimulatedOverviewBody => {\n return {\n period: params.period,\n subjectId: params.subjectId,\n areaKnowledgeId: params.areaKnowledgeId,\n schoolIds: params.schoolIds,\n schoolYearIds: params.schoolYearIds,\n classIds: params.classIds,\n studentsIds: params.studentsIds,\n page: params.page ?? 1,\n limit: params.limit ?? 10,\n orderBy: params.orderBy ?? 'name',\n order: params.order ?? 'asc',\n };\n };\n\n const fetchOverview = useCallback(\n async (params: SimulatedOverviewParams, refresh = false) => {\n const currentRequestId = ++requestIdRef.current;\n\n try {\n if (refresh) {\n setIsRefreshing(true);\n } else {\n setLoading(true);\n }\n setError(null);\n\n const endpoint = buildEndpoint(params.simulationType, params.scoreType);\n const body = paramsToBody(params);\n\n const response = await api.post<SimulatedOverviewApiResponse>(\n endpoint,\n body\n );\n\n // Ignore stale responses\n if (currentRequestId !== requestIdRef.current) return;\n\n setData(response.data.data);\n } catch (err) {\n // Ignore errors from stale requests\n if (currentRequestId !== requestIdRef.current) return;\n\n setError(\n getErrorMessage(\n err,\n 'Não foi possível carregar os dados de simulados'\n )\n );\n } finally {\n // Only update loading state for current request\n if (currentRequestId === requestIdRef.current) {\n setLoading(false);\n setIsRefreshing(false);\n }\n }\n },\n [api]\n );\n\n const reset = useCallback(() => {\n // Invalidate any in-flight requests\n requestIdRef.current++;\n setData(initialState.data);\n setLoading(initialState.loading);\n setIsRefreshing(initialState.isRefreshing);\n setError(initialState.error);\n }, []);\n\n return { data, loading, isRefreshing, error, fetchOverview, reset };\n}\n","import type { BaseApiClient } from '../../types/api';\n\n/**\n * Performance tag for simulated exams\n */\nexport enum SimulatedPerformanceTag {\n HIGHLIGHT = 'HIGHLIGHT',\n ABOVE_AVERAGE = 'ABOVE_AVERAGE',\n BELOW_AVERAGE = 'BELOW_AVERAGE',\n ATTENTION_POINT = 'ATTENTION_POINT',\n}\n\n/**\n * Tag config for display\n */\nexport enum SimulatedPerformanceTagVariant {\n SUCCESS = 'success',\n WARNING = 'warning',\n ERROR = 'error',\n INFO = 'info',\n}\n\n/**\n * Tag config for display\n */\nexport interface SimulatedPerformanceTagConfig {\n label: string;\n variant: SimulatedPerformanceTagVariant;\n}\n\n/**\n * Map performance to tag config\n */\nexport const SIMULATED_PERFORMANCE_TAG_CONFIG: Record<\n SimulatedPerformanceTag,\n SimulatedPerformanceTagConfig\n> = {\n [SimulatedPerformanceTag.HIGHLIGHT]: {\n label: 'Destaque da turma',\n variant: SimulatedPerformanceTagVariant.SUCCESS,\n },\n [SimulatedPerformanceTag.ABOVE_AVERAGE]: {\n label: 'Acima da média',\n variant: SimulatedPerformanceTagVariant.INFO,\n },\n [SimulatedPerformanceTag.BELOW_AVERAGE]: {\n label: 'Abaixo da média',\n variant: SimulatedPerformanceTagVariant.WARNING,\n },\n [SimulatedPerformanceTag.ATTENTION_POINT]: {\n label: 'Ponto de atenção',\n variant: SimulatedPerformanceTagVariant.ERROR,\n },\n};\n\n/**\n * Badge action enum for performance status indicators\n */\nexport enum PerformanceBadgeAction {\n SUCCESS = 'success',\n INFO = 'info',\n WARNING = 'warning',\n ERROR = 'error',\n}\n\n/**\n * Map performance tag to Badge action type\n */\nexport const PERFORMANCE_TAG_TO_BADGE_ACTION: Record<\n SimulatedPerformanceTag,\n PerformanceBadgeAction\n> = {\n [SimulatedPerformanceTag.HIGHLIGHT]: PerformanceBadgeAction.SUCCESS,\n [SimulatedPerformanceTag.ABOVE_AVERAGE]: PerformanceBadgeAction.INFO,\n [SimulatedPerformanceTag.BELOW_AVERAGE]: PerformanceBadgeAction.WARNING,\n [SimulatedPerformanceTag.ATTENTION_POINT]: PerformanceBadgeAction.ERROR,\n};\n\n/**\n * Simulation type used in reports\n */\nexport enum ReportSimulationType {\n ENEM_1 = 'enem-1',\n ENEM_2 = 'enem-2',\n ESSAYS = 'essays',\n}\n\n/**\n * Backward-compatible simulation type alias\n */\nexport type SimulationType = `${ReportSimulationType}`;\n\n/**\n * Student info for details modal\n */\nexport interface StudentDetailsInfo {\n studentId: string;\n institutionId: string;\n name: string;\n school: string;\n schoolYear: string;\n class: string;\n average: number;\n performance: SimulatedPerformanceTag;\n}\n\n/**\n * Subject performance item (level 1)\n */\nexport interface SubjectPerformanceItem {\n id: string;\n name: string;\n color: string | null;\n icon: string | null;\n questionsCount: number;\n performance: {\n correct: number;\n incorrect: number;\n correctPercentage: number;\n };\n}\n\n/**\n * Content performance item (level 2)\n */\nexport interface StudentContentPerformanceItem {\n contentId: string;\n contentName: string;\n bnccCode: string | null;\n questionsCount: number;\n performance: {\n correct: number;\n incorrect: number;\n correctPercentage: number;\n };\n}\n\n/**\n * Response data when subjectId is null (list of subjects)\n */\nexport interface StudentSubjectsData {\n student: StudentDetailsInfo;\n subjects: SubjectPerformanceItem[];\n page: number;\n limit: number;\n total: number;\n}\n\n/**\n * Response data when subjectId is provided (list of contents)\n */\nexport interface StudentContentsData {\n student: StudentDetailsInfo;\n subject: {\n id: string;\n name: string;\n };\n contents: StudentContentPerformanceItem[];\n page: number;\n limit: number;\n total: number;\n}\n\n/**\n * Union type for student details response\n */\nexport type StudentDetailsData = StudentSubjectsData | StudentContentsData;\n\n/**\n * Type guard to check if data is StudentSubjectsData\n */\nexport function isStudentSubjectsData(\n data: StudentDetailsData\n): data is StudentSubjectsData {\n return 'subjects' in data;\n}\n\n/**\n * Type guard to check if data is StudentContentsData\n */\nexport function isStudentContentsData(\n data: StudentDetailsData\n): data is StudentContentsData {\n return 'contents' in data;\n}\n\n/**\n * Activity filters for endpoint\n */\nexport interface ActivityFilters {\n types?: string[];\n subtypes?: string[];\n statuses?: string[];\n}\n\n/**\n * Convert SimulationType to ActivityFilters\n */\nexport function simulationTypeToActivityFilters(\n simulationType: SimulationType\n): ActivityFilters {\n switch (simulationType) {\n case ReportSimulationType.ENEM_1:\n return {\n types: ['SIMULADO'],\n subtypes: ['ENEM_PROVA_1'],\n };\n case ReportSimulationType.ENEM_2:\n return {\n types: ['SIMULADO'],\n subtypes: ['ENEM_PROVA_2'],\n };\n default:\n return {};\n }\n}\n\n/**\n * Parameters for fetching student details\n */\nexport interface StudentDetailsParams {\n simulationType: SimulationType;\n userInstitutionId: string;\n period: string;\n subjectId?: string | null;\n page?: number;\n limit?: number;\n}\n\n/**\n * API response structure\n */\nexport interface StudentDetailsApiResponse {\n message: string;\n data: StudentDetailsData;\n}\n\n/**\n * Hook state\n */\nexport interface UseSimulatedStudentDetailsState {\n data: StudentDetailsData | null;\n loading: boolean;\n error: string | null;\n}\n\n/**\n * Hook return type\n */\nexport interface UseSimulatedStudentDetailsReturn extends UseSimulatedStudentDetailsState {\n fetchDetails: (params: StudentDetailsParams) => Promise<void>;\n reset: () => void;\n}\n\n/**\n * Modal props\n */\nexport interface SimulatedStudentDetailsModalProps {\n readonly api: BaseApiClient;\n readonly isOpen: boolean;\n readonly onClose: () => void;\n readonly simulationType: SimulationType;\n readonly userInstitutionId: string | null;\n readonly studentName?: string;\n readonly period: string;\n}\n","import type {\n AggregatedOverviewData,\n StudentsOnlyOverviewData,\n ClassesOverviewData,\n MunicipalitiesOverviewData,\n OverviewAggregationType,\n} from './types';\n\n/**\n * Extract error message from an unknown error\n *\n * @param err - The caught error\n * @param fallbackMessage - Default message if error is not an Error instance\n * @returns The error message string\n */\nexport function getErrorMessage(err: unknown, fallbackMessage: string): string {\n return err instanceof Error ? err.message : fallbackMessage;\n}\n\n/**\n * Type guard to check if data is StudentsOnlyOverviewData\n * Verifies both type parameter and data shape (absence of class/municipality-specific fields)\n */\nexport function isStudentsData(\n data: AggregatedOverviewData | null,\n type: OverviewAggregationType\n): data is StudentsOnlyOverviewData {\n return (\n type === 'students' &&\n data !== null &&\n !('totalClasses' in data) &&\n !('totalMunicipalities' in data)\n );\n}\n\n/**\n * Type guard to check if data is ClassesOverviewData\n * Verifies both type parameter and data shape (presence of totalClasses)\n */\nexport function isClassesData(\n data: AggregatedOverviewData | null,\n type: OverviewAggregationType\n): data is ClassesOverviewData {\n return type === 'classes' && data !== null && 'totalClasses' in data;\n}\n\n/**\n * Type guard to check if data is MunicipalitiesOverviewData\n * Verifies both type parameter and data shape (presence of totalMunicipalities)\n */\nexport function isMunicipalitiesData(\n data: AggregatedOverviewData | null,\n type: OverviewAggregationType\n): data is MunicipalitiesOverviewData {\n return (\n type === 'municipalities' && data !== null && 'totalMunicipalities' in data\n );\n}\n","import { useState, useCallback, useRef } from 'react';\nimport type { BaseApiClient } from '../../types/api';\nimport type {\n AggregatedOverviewData,\n AggregatedOverviewParams,\n StudentsOverviewApiResponse,\n ClassesOverviewApiResponse,\n MunicipalitiesOverviewApiResponse,\n UseAggregatedOverviewState,\n UseAggregatedOverviewReturn,\n OverviewAggregationType,\n} from './types';\nimport {\n simulationTypeToActivityFilters,\n SimulationType,\n} from '../SimulatedStudentDetailsModal/types';\nimport { getErrorMessage } from './utils';\n\n/**\n * Body type for POST endpoints\n */\ninterface AggregatedOverviewBody {\n period?: string;\n subjectId?: string;\n areaKnowledgeId?: string;\n schoolIds?: string[];\n schoolYearIds?: string[];\n classIds?: string[];\n studentsIds?: string[];\n}\n\n/**\n * Build the API endpoint based on aggregation type\n */\nfunction buildEndpoint(\n aggregationType: OverviewAggregationType,\n simulationType: SimulationType,\n scoreType?: string\n): string {\n // Essays use the legacy endpoint\n if (simulationType === 'essays') {\n return '/performance/simulated/essays/students-overview';\n }\n\n const activityFilters = simulationTypeToActivityFilters(simulationType);\n const params = new URLSearchParams();\n\n // Add aggregationType as query param (unified endpoint)\n params.append('aggregationType', aggregationType);\n\n activityFilters.types?.forEach((t) => params.append('types', t));\n activityFilters.statuses?.forEach((s) => params.append('statuses', s));\n\n if (scoreType && scoreType !== 'percentage') {\n params.append('scoreType', scoreType);\n }\n\n const basePath = '/performance/simulated/activities/overview';\n return `${basePath}?${params.toString()}`;\n}\n\n/**\n * Initial state for the hook\n */\nconst initialState: UseAggregatedOverviewState = {\n data: null,\n loading: false,\n isRefreshing: false,\n error: null,\n};\n\n/**\n * Hook for fetching aggregated overview data (without students list)\n * Supports different aggregation types: students, classes, municipalities\n *\n * @param api - API client with post method\n *\n * @example\n * ```tsx\n * const { data, loading, error, fetchOverview } = useAggregatedOverview(api);\n *\n * // For students overview\n * fetchOverview({\n * aggregationType: 'students',\n * simulationType: 'enem-1',\n * period: '1_MONTH',\n * });\n *\n * // For classes overview (UNIT_MANAGER)\n * fetchOverview({\n * aggregationType: 'classes',\n * simulationType: 'enem-1',\n * period: '1_MONTH',\n * });\n *\n * // For municipalities overview (REGIONAL_MANAGER)\n * fetchOverview({\n * aggregationType: 'municipalities',\n * simulationType: 'enem-1',\n * period: '1_MONTH',\n * });\n * ```\n */\nexport function useAggregatedOverview(\n api: BaseApiClient\n): UseAggregatedOverviewReturn {\n const [data, setData] = useState<AggregatedOverviewData | null>(\n initialState.data\n );\n const [loading, setLoading] = useState(initialState.loading);\n const [isRefreshing, setIsRefreshing] = useState(initialState.isRefreshing);\n const [error, setError] = useState<string | null>(initialState.error);\n\n // Track request ID to ignore stale responses\n const requestIdRef = useRef(0);\n\n /**\n * Convert params to POST body format\n */\n const paramsToBody = (\n params: AggregatedOverviewParams\n ): AggregatedOverviewBody => {\n return {\n period: params.period,\n subjectId: params.subjectId,\n areaKnowledgeId: params.areaKnowledgeId,\n schoolIds: params.schoolIds,\n schoolYearIds: params.schoolYearIds,\n classIds: params.classIds,\n studentsIds: params.studentsIds,\n };\n };\n\n const fetchOverview = useCallback(\n async (params: AggregatedOverviewParams, refresh = false) => {\n const currentRequestId = ++requestIdRef.current;\n\n try {\n if (refresh) {\n setIsRefreshing(true);\n } else {\n setLoading(true);\n }\n setError(null);\n\n const endpoint = buildEndpoint(\n params.aggregationType,\n params.simulationType,\n params.scoreType\n );\n const body = paramsToBody(params);\n\n let responseData: AggregatedOverviewData;\n\n switch (params.aggregationType) {\n case 'classes': {\n const response = await api.post<ClassesOverviewApiResponse>(\n endpoint,\n body\n );\n responseData = response.data.data;\n break;\n }\n case 'municipalities': {\n const response = await api.post<MunicipalitiesOverviewApiResponse>(\n endpoint,\n body\n );\n responseData = response.data.data;\n break;\n }\n case 'students':\n default: {\n const response = await api.post<StudentsOverviewApiResponse>(\n endpoint,\n body\n );\n responseData = response.data.data;\n break;\n }\n }\n\n // Ignore stale responses\n if (currentRequestId !== requestIdRef.current) return;\n\n setData(responseData);\n } catch (err) {\n // Ignore errors from stale requests\n if (currentRequestId !== requestIdRef.current) return;\n\n setError(\n getErrorMessage(err, 'Não foi possível carregar os dados de overview')\n );\n } finally {\n // Only update loading state for current request\n if (currentRequestId === requestIdRef.current) {\n setLoading(false);\n setIsRefreshing(false);\n }\n }\n },\n [api]\n );\n\n const reset = useCallback(() => {\n // Invalidate any in-flight requests\n requestIdRef.current++;\n setData(initialState.data);\n setLoading(initialState.loading);\n setIsRefreshing(initialState.isRefreshing);\n setError(initialState.error);\n }, []);\n\n return { data, loading, isRefreshing, error, fetchOverview, reset };\n}\n","import { useState, useCallback, useRef } from 'react';\nimport {\n ReportSimulationType,\n simulationTypeToActivityFilters,\n type SimulationType,\n} from '../SimulatedStudentDetailsModal/types';\nimport type { BaseApiClient } from '../../types/api';\nimport type {\n SimulatedContentsParams,\n ContentsPerformanceData,\n ContentsPerformanceApiResponse,\n UseSimulatedContentsReturn,\n} from './types';\n\n/**\n * Build the API endpoint with activity filters and scoreType as query params\n */\nfunction buildEndpoint(\n simulationType: SimulationType,\n scoreType?: 'percentage' | 'tri'\n): string | null {\n // Essays don't have contents/skills\n if (simulationType === ReportSimulationType.ESSAYS) {\n return null;\n }\n\n const activityFilters = simulationTypeToActivityFilters(simulationType);\n const params = new URLSearchParams();\n\n activityFilters.types?.forEach((t) => params.append('types', t));\n // Note: subtypes are not sent - filtering is done via subjectId/areaKnowledgeId in the body\n activityFilters.statuses?.forEach((s) => params.append('statuses', s));\n\n if (scoreType && scoreType !== 'percentage') {\n params.append('scoreType', scoreType);\n }\n\n const queryString = params.toString();\n const endpoint = '/performance/simulated/activities/contents-performance';\n\n if (!queryString) {\n return endpoint;\n }\n\n return `${endpoint}?${queryString}`;\n}\n\n/**\n * Hook for fetching contents/skills performance in simulated exams\n *\n * @param api - API client with post method\n *\n * @example\n * ```tsx\n * const { data, loading, fetchContents } = useSimulatedContents(api);\n *\n * useEffect(() => {\n * fetchContents({\n * simulationType: 'enem-1',\n * period: 'ONE_MONTH',\n * page: 1,\n * limit: 10,\n * });\n * }, [fetchContents]);\n * ```\n */\nexport function useSimulatedContents(\n api: BaseApiClient\n): UseSimulatedContentsReturn {\n const [data, setData] = useState<ContentsPerformanceData | null>(null);\n const [loading, setLoading] = useState(false);\n const [isRefreshing, setIsRefreshing] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const isFirstLoad = useRef(true);\n const latestRequestId = useRef(0);\n const isCurrentRequest = (requestId: number) =>\n requestId === latestRequestId.current;\n\n const buildRequestBody = (params: SimulatedContentsParams) => ({\n period: params.period,\n subjectId:\n params.subjectId && params.subjectId !== 'all'\n ? params.subjectId\n : undefined,\n areaKnowledgeId:\n params.areaKnowledgeId && params.areaKnowledgeId !== 'all'\n ? params.areaKnowledgeId\n : undefined,\n schoolIds: params.schoolIds,\n schoolYearIds: params.schoolYearIds,\n classIds: params.classIds,\n studentsIds: params.studentsIds,\n page: params.page ?? 1,\n limit: params.limit ?? 10,\n orderBy: params.orderBy ?? 'correctPercentage',\n order: params.order ?? 'asc',\n });\n\n const handleNoEndpoint = (requestId: number) => {\n if (!isCurrentRequest(requestId)) {\n return;\n }\n setData(null);\n setError(null);\n setLoading(false);\n setIsRefreshing(false);\n isFirstLoad.current = true;\n };\n\n const startRequestState = (requestId: number, refresh: boolean) => {\n if (!isCurrentRequest(requestId)) {\n return;\n }\n if (isFirstLoad.current && !refresh) {\n setLoading(true);\n } else {\n setIsRefreshing(true);\n }\n setError(null);\n };\n\n const handleSuccess = (\n requestId: number,\n response: { data: ContentsPerformanceApiResponse }\n ) => {\n if (!isCurrentRequest(requestId)) {\n return;\n }\n setData(response.data.data);\n isFirstLoad.current = false;\n };\n\n const handleError = (requestId: number, err: unknown) => {\n if (!isCurrentRequest(requestId)) {\n return;\n }\n const errorMessage =\n err instanceof Error\n ? err.message\n : 'Erro ao carregar dados de habilidades';\n setError(errorMessage);\n setData(null);\n };\n\n const finishRequestState = (requestId: number) => {\n if (!isCurrentRequest(requestId)) {\n return;\n }\n setLoading(false);\n setIsRefreshing(false);\n };\n\n const fetchContents = useCallback(\n async (params: SimulatedContentsParams, refresh = false) => {\n const localRequestId = ++latestRequestId.current;\n const endpoint = buildEndpoint(params.simulationType, params.scoreType);\n\n // Essays don't have contents\n if (!endpoint) {\n handleNoEndpoint(localRequestId);\n return;\n }\n\n startRequestState(localRequestId, refresh);\n\n try {\n const response = await api.post<ContentsPerformanceApiResponse>(\n endpoint,\n buildRequestBody(params)\n );\n handleSuccess(localRequestId, response);\n } catch (err) {\n handleError(localRequestId, err);\n } finally {\n finishRequestState(localRequestId);\n }\n },\n [api]\n );\n\n const reset = useCallback(() => {\n setData(null);\n setLoading(false);\n setIsRefreshing(false);\n setError(null);\n isFirstLoad.current = true;\n }, []);\n\n return {\n data,\n loading,\n isRefreshing,\n error,\n fetchContents,\n reset,\n };\n}\n","import { useMemo } from 'react';\nimport Select, {\n SelectTrigger,\n SelectValue,\n SelectContent,\n SelectItem,\n} from '../Select/Select';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\nimport { GridFourIcon } from '@phosphor-icons/react/dist/csr/GridFour';\nimport type { AreaKnowledgeSelectorProps } from './types';\nimport type { AreaKnowledgePerformance } from '../GeneralOverviewSection/types';\nimport { ESSAY_AREA_ID } from './types';\n\ninterface SelectItemData {\n id: string;\n name: string;\n color?: string;\n}\n\n/**\n * AreaKnowledgeSelector - Select dropdown for knowledge areas\n *\n * Allows filtering by area of knowledge (Área de Conhecimento).\n * Shows \"Todos\" option plus all available areas from the general overview.\n * Optionally includes \"Redação\" (essay) option at the end.\n *\n * @example\n * ```tsx\n * <AreaKnowledgeSelector\n * areas={generalOverviewData?.areas || []}\n * selectedAreaId={selectedAreaKnowledgeId}\n * onAreaChange={handleAreaKnowledgeChange}\n * loading={generalOverviewLoading}\n * />\n * ```\n */\nexport function AreaKnowledgeSelector({\n areas,\n selectedAreaId,\n onAreaChange,\n loading = false,\n label = 'Área de conhecimento',\n includeEssay = true,\n}: AreaKnowledgeSelectorProps) {\n // Build select items with \"Todos\" option and optionally \"Redação\" at the end\n const selectItems = useMemo((): SelectItemData[] => {\n const allOption: SelectItemData = {\n id: 'all',\n name: 'Todos',\n };\n\n const areaItems: SelectItemData[] = areas.map(\n (area: AreaKnowledgePerformance) => ({\n id: area.id,\n name: area.name,\n color: area.color,\n })\n );\n\n const items = [allOption, ...areaItems];\n\n if (includeEssay) {\n const essayOption: SelectItemData = {\n id: ESSAY_AREA_ID,\n name: 'Redação',\n };\n items.push(essayOption);\n }\n\n return items;\n }, [areas, includeEssay]);\n\n // Handle value change\n const handleValueChange = (value: string) => {\n if (value === 'all') {\n onAreaChange(null);\n } else {\n onAreaChange(value);\n }\n };\n\n const effectiveValue = selectedAreaId || 'all';\n\n return (\n <div className=\"space-y-2\">\n <Text size=\"sm\" weight=\"medium\" className=\"text-text-700\">\n {label}\n </Text>\n <div className=\"relative\">\n <Select\n defaultValue=\"all\"\n value={effectiveValue}\n onValueChange={handleValueChange}\n >\n <SelectTrigger\n className={cn('w-full', loading && 'opacity-50')}\n disabled={loading}\n >\n <SelectValue placeholder=\"Selecione uma área\" />\n </SelectTrigger>\n <SelectContent>\n {selectItems.map((item: SelectItemData) => {\n let itemIcon = null;\n\n if (item.id === 'all') {\n itemIcon = (\n <span className=\"w-[21px] h-[21px] flex items-center justify-center\">\n <GridFourIcon\n size={17}\n weight=\"bold\"\n className=\"text-gray-600\"\n />\n </span>\n );\n } else if (item.color) {\n itemIcon = (\n <span\n className=\"w-[21px] h-[21px] flex items-center justify-center rounded-sm\"\n style={{ backgroundColor: `${item.color}20` }}\n >\n <span\n className=\"w-2 h-2 rounded-full\"\n style={{ backgroundColor: item.color }}\n />\n </span>\n );\n }\n\n return (\n <SelectItem key={item.id} value={item.id} disabled={loading}>\n <div className=\"flex items-center gap-2\">\n {itemIcon}\n <span className=\"whitespace-nowrap\">{item.name}</span>\n </div>\n </SelectItem>\n );\n })}\n </SelectContent>\n </Select>\n </div>\n </div>\n );\n}\n\nexport default AreaKnowledgeSelector;\n","// Reuse types from GeneralOverviewSection\nimport type { AreaKnowledgePerformance } from '../GeneralOverviewSection/types';\n\n/**\n * Special ID for essay (Redação) area\n */\nexport const ESSAY_AREA_ID = 'essay';\n\n/**\n * Props for AreaKnowledgeSelector component\n */\nexport interface AreaKnowledgeSelectorProps {\n /** List of areas from general overview (only id and name are used) */\n readonly areas: AreaKnowledgePerformance[];\n /** Currently selected area ID (null means \"Todos\") */\n readonly selectedAreaId: string | null;\n /** Callback when area changes */\n readonly onAreaChange: (areaId: string | null) => void;\n /** Loading state */\n readonly loading?: boolean;\n /** Label text (default: \"Área de conhecimento\") */\n readonly label?: string;\n /** Include essay option at the end (default: true) */\n readonly includeEssay?: boolean;\n}\n","import { useEffect, useState, useCallback, type ReactNode } from 'react';\nimport ReportDetailModal from '../ReportDetailModal/ReportDetailModal';\nimport Text from '../Text/Text';\nimport ProgressBar from '../ProgressBar/ProgressBar';\nimport Badge from '../Badge/Badge';\nimport { ArrowLeftIcon } from '@phosphor-icons/react/dist/csr/ArrowLeft';\nimport { CaretRightIcon } from '@phosphor-icons/react/dist/csr/CaretRight';\nimport { useSimulatedStudentDetails } from './useSimulatedStudentDetails';\nimport { formatPercentageRounded } from '../../utils/utils';\nimport { downloadExcel } from '../../utils/exportExcel';\nimport { formatDateForFileName } from '../../utils/exportFormat';\nimport { buildSimulatedStudentDetailsSheets } from './exportSheets';\nimport {\n isStudentSubjectsData,\n SIMULATED_PERFORMANCE_TAG_CONFIG,\n PERFORMANCE_TAG_TO_BADGE_ACTION,\n type SimulatedStudentDetailsModalProps,\n type SubjectPerformanceItem,\n type StudentContentPerformanceItem,\n} from './types';\nimport Button from '../Button/Button';\n\n/**\n * Prefixo do arquivo exportado, em PDF e em XLSX. Recebe a data do dia.\n *\n * Sem o nome do estudante nem o da matéria de propósito: os dois trazem\n * acentos, espaços e às vezes pontuação, e um nome de arquivo pede\n * transliteração — que não existe nesta lib e que não vale a pena inventar\n * aqui. É o mesmo prefixo nos dois níveis da cascata; quem diz de qual nível é\n * o arquivo são as abas da planilha (o nome da segunda muda) e o título\n * impresso no PDF.\n */\nconst EXPORT_FILE_PREFIX = 'desempenho-simulado';\n\n/**\n * Modal for displaying student performance details in simulated exams\n * Supports cascading navigation: Subjects (level 1) -> Contents (level 2)\n *\n * Exportável: monta sobre o `ReportDetailModal`, então traz o botão \"Baixar\n * relatório\" com PDF (impressão só deste modal) e XLSX. As duas saídas usam o\n * dado que ESTE componente já buscou por `useSimulatedStudentDetails` — nenhuma\n * requisição a mais é disparada para exportar — e a planilha espelha os blocos\n * da tela aba a aba (veja `exportSheets.ts`).\n *\n * CASCATA: a planilha leva o nível VISÍVEL, um só. O hook guarda um `data`\n * único e o troca a cada navegação, então o outro nível não está em memória;\n * levá-lo junto exigiria requisições que a tela não fez. Ver a justificativa\n * completa em `exportSheets.ts`.\n *\n * ESTADOS DE CARREGANDO, ERRO E SEM-DADO: o botão continua lá, porque é o\n * `ReportDetailModal` que o desenha e ele não tem como escondê-lo. O PDF sai com\n * o que estiver na tela (a mensagem de carregando, a de erro ou a de\n * \"Nenhum dado encontrado\") e o XLSX sai com as duas abas só de cabeçalho, já\n * que `data` é `null` — nenhum dos dois estoura.\n *\n * O consumidor precisa importar `analytica-frontend-lib/print.css`: sem as\n * regras `@media print` o PDF sai com a página inteira em vez do modal.\n */\nexport function SimulatedStudentDetailsModal({\n api,\n isOpen,\n onClose,\n simulationType,\n userInstitutionId,\n studentName,\n period,\n}: SimulatedStudentDetailsModalProps) {\n const { data, loading, error, fetchDetails, reset } =\n useSimulatedStudentDetails(api);\n const [selectedSubject, setSelectedSubject] = useState<{\n id: string;\n name: string;\n } | null>(null);\n\n // Fetch subjects when modal opens\n useEffect(() => {\n if (isOpen && userInstitutionId) {\n fetchDetails({\n simulationType,\n userInstitutionId,\n period,\n subjectId: null,\n });\n }\n }, [isOpen, userInstitutionId, simulationType, period, fetchDetails]);\n\n // Reset state when modal closes\n useEffect(() => {\n if (!isOpen) {\n reset();\n setSelectedSubject(null);\n }\n }, [isOpen, reset]);\n\n // Handle subject click (navigate to level 2)\n const handleSubjectClick = useCallback(\n (subject: SubjectPerformanceItem) => {\n if (!userInstitutionId) return;\n\n setSelectedSubject({ id: subject.id, name: subject.name });\n fetchDetails({\n simulationType,\n userInstitutionId,\n period,\n subjectId: subject.id,\n });\n },\n [userInstitutionId, simulationType, period, fetchDetails]\n );\n\n // Handle back navigation (return to level 1)\n const handleBack = useCallback(() => {\n if (!userInstitutionId) return;\n\n setSelectedSubject(null);\n fetchDetails({\n simulationType,\n userInstitutionId,\n period,\n subjectId: null,\n });\n }, [userInstitutionId, simulationType, period, fetchDetails]);\n\n // O nome que o TÍTULO do modal exibe no nível 2 — e, por isso, o que a aba de\n // resumo da planilha carrega. Sai do estado local, não de\n // `StudentContentsData.subject`, para que o arquivo diga o mesmo que a tela.\n const selectedSubjectName = selectedSubject?.name ?? null;\n\n const fileName = `${EXPORT_FILE_PREFIX}-${formatDateForFileName(new Date())}`;\n\n const handleDownloadExcel = useCallback(() => {\n downloadExcel(\n fileName,\n buildSimulatedStudentDetailsSheets(data, selectedSubjectName)\n );\n }, [fileName, data, selectedSubjectName]);\n\n // Build modal title with back button when in level 2\n const modalTitle = selectedSubject ? (\n <div className=\"flex items-center gap-2\">\n {/* `data-print-hide`: navegar é controle de tela, não conteúdo do\n relatório, e no caminho PDF este botão está DENTRO da região impressa.\n Fica no próprio <Button> porque ele É o item do flex — escondido, o\n `gap-2` não separa mais nada, que é o que o contrato do print.css\n quer evitar. O nome da matéria ao lado continua saindo no papel. */}\n <Button\n onClick={handleBack}\n variant=\"raw\"\n className=\"p-1 hover:bg-background-100 rounded-md transition-colors\"\n aria-label=\"Voltar para lista de componentes curriculares\"\n data-print-hide\n >\n <ArrowLeftIcon size={20} className=\"text-text-600\" />\n </Button>\n <Text>{selectedSubject.name}</Text>\n </div>\n ) : (\n `Desempenho de ${studentName || 'Estudante'}`\n );\n\n const renderStatusMessage = (message: ReactNode, className: string) => (\n <div className=\"flex items-center justify-center py-8\">\n <Text size=\"sm\" className={className}>\n {message}\n </Text>\n </div>\n );\n\n const renderDetailsContent = (\n details: NonNullable<typeof data>\n ): ReactNode => {\n if (isStudentSubjectsData(details)) {\n if (details.subjects.length > 0) {\n return details.subjects.map((subject) => (\n <SubjectItem\n key={subject.id}\n subject={subject}\n onClick={() => handleSubjectClick(subject)}\n questionsLabel=\"questões\"\n />\n ));\n }\n\n return (\n <Text size=\"sm\" className=\"text-text-500 text-center py-4\">\n Nenhum componente curricular encontrado\n </Text>\n );\n }\n\n if (details.contents.length > 0) {\n return details.contents.map((content) => (\n <ContentItem\n key={content.contentId}\n content={content}\n questionsLabel=\"questões\"\n ofLabel=\"de\"\n />\n ));\n }\n\n return (\n <Text size=\"sm\" className=\"text-text-500 text-center py-4\">\n Nenhuma habilidade encontrada\n </Text>\n );\n };\n\n // Os quatro estados da tela dividem a MESMA casca de modal: antes cada um\n // devolvia o seu próprio `Modal`, o que agora daria quatro `ReportDetailModal`\n // e, com eles, um botão de download que aparece e some conforme o dado chega.\n const renderModalContent = (): ReactNode => {\n if (loading) {\n return renderStatusMessage('Carregando...', 'text-text-500');\n }\n\n if (error) {\n return renderStatusMessage(error, 'text-error-500');\n }\n\n if (!data) {\n return renderStatusMessage('Nenhum dado encontrado', 'text-text-500');\n }\n\n return (\n <div className=\"flex flex-col gap-4\">\n {/* Student header card */}\n <div className=\"flex items-center justify-between p-4 bg-background-50 rounded-xl\">\n <div className=\"flex flex-col gap-1\">\n <Text size=\"md\" weight=\"semibold\" className=\"text-text-950\">\n {data.student.name}\n </Text>\n <Text size=\"sm\" className=\"text-text-500\">\n {data.student.school} - {data.student.class}\n </Text>\n </div>\n <div className=\"flex flex-col items-end gap-1\">\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n {formatPercentageRounded(data.student.average)}\n </Text>\n <Badge\n variant=\"solid\"\n action={PERFORMANCE_TAG_TO_BADGE_ACTION[data.student.performance]}\n size=\"small\"\n >\n {SIMULATED_PERFORMANCE_TAG_CONFIG[data.student.performance].label}\n </Badge>\n </div>\n </div>\n\n {/* List of subjects or contents */}\n <div className=\"flex flex-col gap-2 max-h-[400px] overflow-y-auto\">\n {renderDetailsContent(data)}\n </div>\n </div>\n );\n };\n\n return (\n <ReportDetailModal\n isOpen={isOpen}\n onClose={onClose}\n title={modalTitle}\n size=\"lg\"\n // Só `fileName`: o PDF é a impressão deste modal, e o `ReportDetailModal`\n // já a faz. Ligar `onDownloadPdf` a um `useReportPrint` local somaria uma\n // segunda impressão — o callback SUBSTITUI a embutida, então o usuário\n // veria dois diálogos.\n fileName={fileName}\n onDownloadExcel={handleDownloadExcel}\n >\n {renderModalContent()}\n </ReportDetailModal>\n );\n}\n\n/**\n * Subject item component (level 1)\n */\nfunction SubjectItem({\n subject,\n onClick,\n questionsLabel,\n}: Readonly<{\n subject: SubjectPerformanceItem;\n onClick: () => void;\n questionsLabel: string;\n}>) {\n return (\n <button\n onClick={onClick}\n className=\"flex items-center gap-4 p-4 bg-background border border-border-50 rounded-xl hover:bg-background-50 transition-colors text-left w-full\"\n /* A linha é `<button>` porque desce para o nível 2, mas o TEXTO dela é o\n conteúdo do relatório: nome da matéria, questões e percentual. Por isso\n ela NÃO leva `data-print-hide` — escondê-la apagaria a lista de matérias\n do PDF. O testid existe para o invariante de impressão poder declará-la\n como impressa de propósito (veja `testing/printRegionInvariant.ts`); sem\n ele a linha não tem atributo nenhum que a distinga de um controle. */\n data-testid=\"subject-performance-row\"\n >\n {/* Subject color indicator */}\n {subject.color && (\n <div\n className=\"w-3 h-3 rounded-full flex-shrink-0\"\n style={{ backgroundColor: subject.color }}\n />\n )}\n\n {/* Subject info */}\n <div className=\"flex-1 min-w-0\">\n <Text size=\"sm\" weight=\"semibold\" className=\"text-text-950 truncate\">\n {subject.name}\n </Text>\n <Text size=\"xs\" className=\"text-text-500\">\n {subject.questionsCount} {questionsLabel}\n </Text>\n </div>\n\n {/* Progress bar */}\n <div className=\"w-32 flex-shrink-0\">\n <ProgressBar\n value={subject.performance.correctPercentage}\n variant=\"green\"\n size=\"small\"\n showPercentage\n />\n </div>\n\n {/* Arrow indicator */}\n <CaretRightIcon size={16} className=\"text-text-400 flex-shrink-0\" />\n </button>\n );\n}\n\n/**\n * Content item component (level 2)\n */\nfunction ContentItem({\n content,\n questionsLabel,\n ofLabel,\n}: Readonly<{\n content: StudentContentPerformanceItem;\n questionsLabel: string;\n ofLabel: string;\n}>) {\n return (\n <div className=\"flex items-center gap-4 p-4 bg-background border border-border-50 rounded-xl\">\n {/* Content info */}\n <div className=\"flex-1 min-w-0\">\n <Text size=\"sm\" weight=\"semibold\" className=\"text-text-950 truncate\">\n {content.contentName}\n </Text>\n <div className=\"flex items-center gap-2\">\n {content.bnccCode && (\n <Text size=\"xs\" className=\"text-primary-600\">\n {content.bnccCode}\n </Text>\n )}\n <Text size=\"xs\" className=\"text-text-500\">\n {content.questionsCount} {questionsLabel}\n </Text>\n </div>\n </div>\n\n {/* Progress bar */}\n <div className=\"w-32 flex-shrink-0\">\n <ProgressBar\n value={content.performance.correctPercentage}\n variant=\"green\"\n size=\"small\"\n showPercentage\n />\n </div>\n\n {/* Hit count */}\n <div className=\"flex items-center gap-1 flex-shrink-0\">\n <Text size=\"sm\" weight=\"semibold\" className=\"text-success-600\">\n {content.performance.correct}\n </Text>\n <Text size=\"xs\" className=\"text-text-500 whitespace-nowrap\">\n {ofLabel} {content.questionsCount}\n </Text>\n </div>\n </div>\n );\n}\n","import { useState, useCallback, useRef } from 'react';\nimport type { BaseApiClient } from '../../types/api';\nimport {\n ReportSimulationType,\n simulationTypeToActivityFilters,\n type SimulationType,\n type StudentDetailsData,\n type StudentDetailsParams,\n type StudentDetailsApiResponse,\n type UseSimulatedStudentDetailsReturn,\n} from './types';\nimport { paramsToBody } from './utils';\n\n/**\n * Build the API endpoint with activity filters as query params\n */\nfunction buildEndpoint(simulationType: SimulationType): string {\n if (simulationType === ReportSimulationType.ESSAYS) {\n return '/performance/simulated/essays/student-details';\n }\n\n const activityFilters = simulationTypeToActivityFilters(simulationType);\n const params = new URLSearchParams();\n\n activityFilters.types?.forEach((t) => params.append('types', t));\n // Note: subtypes are not sent - filtering is done via subjectId in the body\n activityFilters.statuses?.forEach((s) => params.append('statuses', s));\n\n const queryString = params.toString();\n const endpoint = '/performance/simulated/activities/student-details';\n\n if (!queryString) {\n return endpoint;\n }\n\n return `${endpoint}?${queryString}`;\n}\n\n/**\n * Hook for fetching student details in simulated exams\n * Supports cascading navigation: Subjects (level 1) -> Contents (level 2)\n *\n * @param api - API client with post method\n *\n * @example\n * ```tsx\n * const { data, loading, error, fetchDetails, reset } = useSimulatedStudentDetails(api);\n *\n * // Fetch subjects list (level 1)\n * fetchDetails({\n * simulationType: 'enem-1',\n * userInstitutionId: 'institution-uuid',\n * period: '1_MONTH',\n * });\n *\n * // Fetch contents list (level 2)\n * fetchDetails({\n * simulationType: 'enem-1',\n * userInstitutionId: 'institution-uuid',\n * period: '1_MONTH',\n * subjectId: 'subject-uuid',\n * });\n * ```\n */\nexport function useSimulatedStudentDetails(\n api: BaseApiClient\n): UseSimulatedStudentDetailsReturn {\n const [data, setData] = useState<StudentDetailsData | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n // Track request ID to ignore stale responses\n const requestIdRef = useRef(0);\n\n const fetchDetails = useCallback(\n async (params: StudentDetailsParams) => {\n const currentRequestId = ++requestIdRef.current;\n\n try {\n setLoading(true);\n setError(null);\n setData(null);\n\n const endpoint = buildEndpoint(params.simulationType);\n const body = paramsToBody(params);\n\n const response = await api.post<StudentDetailsApiResponse>(\n endpoint,\n body\n );\n\n // Ignore stale responses\n if (currentRequestId !== requestIdRef.current) return;\n\n setData(response.data.data);\n } catch (err) {\n // Ignore errors from stale requests\n if (currentRequestId !== requestIdRef.current) return;\n\n const errorMessage =\n err instanceof Error\n ? err.message\n : 'Não foi possível carregar os detalhes do estudante';\n setData(null);\n setError(errorMessage);\n } finally {\n // Only update loading state for current request\n if (currentRequestId === requestIdRef.current) {\n setLoading(false);\n }\n }\n },\n [api]\n );\n\n const reset = useCallback(() => {\n // Invalidate any in-flight requests\n requestIdRef.current++;\n setData(null);\n setLoading(false);\n setError(null);\n }, []);\n\n return { data, loading, error, fetchDetails, reset };\n}\n","import type { StudentDetailsParams } from './types';\n\n/**\n * Body type for POST request\n */\nexport interface StudentDetailsBody {\n userInstitutionId: string;\n period?: string;\n subjectId?: string | null;\n page?: number;\n limit?: number;\n}\n\n/**\n * Convert params to POST body format\n */\nexport function paramsToBody(params: StudentDetailsParams): StudentDetailsBody {\n return {\n userInstitutionId: params.userInstitutionId,\n period: params.period,\n subjectId: params.subjectId ?? undefined,\n page: params.page ?? 1,\n limit: params.limit ?? 20,\n };\n}\n","import type { ExcelCell, SheetConfig } from '../../utils/exportExcel';\nimport {\n isStudentSubjectsData,\n SIMULATED_PERFORMANCE_TAG_CONFIG,\n type StudentDetailsData,\n} from './types';\n\n/**\n * Montagem das abas do XLSX do `SimulatedStudentDetailsModal`. Funções puras:\n * nada de React, nada de requisição, nada de escrita em disco — quem grava o\n * arquivo é `downloadExcel`.\n *\n * REGRA QUE GOVERNA ESTE ARQUIVO: a planilha traz o que o modal DESENHA, nada\n * mais e nada menos. As abas espelham os blocos do componente:\n *\n * | Bloco na tela | Onde foi parar |\n * | ------------------------------------------------- | ---------------------------- |\n * | título (nome da matéria, só no nível 2) | 1ª linha do resumo |\n * | card do estudante: nome | resumo |\n * | card do estudante: \"escola - turma\" | resumo, duas linhas |\n * | card do estudante: média | resumo |\n * | card do estudante: badge de desempenho | resumo |\n * | lista de matérias (nível 1) | \"Desempenho por matéria\" |\n * | lista de habilidades (nível 2) | \"Desempenho por habilidade\" |\n *\n * NAVEGAÇÃO EM CASCATA: a segunda aba é a do nível VISÍVEL, uma só, nunca as\n * duas. Não é escolha estética — é a única que o princípio permite. O\n * `useSimulatedStudentDetails` guarda um `data` único e o TROCA a cada\n * navegação (`fetchDetails` faz `setData(null)` antes de gravar a resposta,\n * `useSimulatedStudentDetails.ts:83` e `:95`), então enquanto o nível 2 está na\n * tela a lista de matérias já não existe em memória, e vice-versa. Levar os\n * dois níveis exigiria disparar requisições que a tela não fez — uma por\n * matéria, no caso do nível 2, que só carrega a matéria clicada.\n *\n * O QUE FICA DE FORA, e por quê: mapear pelo tipo, e não pelo que a tela\n * desenha, poria na planilha número que o professor nunca viu. Conferido campo\n * a campo no componente:\n *\n * - `StudentDetailsInfo.studentId`, `.institutionId` e `.schoolYear` — o card\n * escreve `{school} - {class}`, e o ano escolar não aparece em lugar nenhum;\n * - `SubjectPerformanceItem.icon` — nunca renderizado;\n * - `SubjectPerformanceItem.color` — vira um ponto colorido, não texto;\n * - `SubjectPerformanceItem.performance.correct` e `.incorrect` — o nível 1\n * mostra só a barra de percentual e a contagem de questões; a contagem de\n * acertos aparece apenas no nível 2 (\"3 de 4\");\n * - `StudentContentPerformanceItem.performance.incorrect` — idem, nenhum nível\n * o escreve;\n * - `page`, `limit` e `total` — paginação de API, sem controle na tela;\n * - `StudentContentsData.subject` — o nome da matéria SAI na planilha, mas pela\n * `subjectName` que o componente passa, não por este campo: quem o título do\n * modal desenha é o `selectedSubject` do estado local\n * (`SimulatedStudentDetailsModal.tsx:100`). Os dois carregam o mesmo nome,\n * vindos da mesma API; a planilha usa o que o usuário lê.\n *\n * SEM ARQUIVO DE REGRA COMPARTILHADA, ao contrário dos dois modais irmãos\n * (`activityProgress.ts`, `lessonProgress.ts`): este modal não tem condição\n * própria por linha para as duas cópias divergirem. A única decisão que a\n * planilha repete da tela é qual nível está aberto, e o predicado dela —\n * `isStudentSubjectsData` — já mora em `types.ts` e já é o que o componente usa\n * (`SimulatedStudentDetailsModal.tsx:145`), então é ele que este arquivo\n * importa.\n */\n\n/**\n * Nome da aba de indicadores: 19 caracteres.\n *\n * Sem rótulo próprio na tela — o título do modal é `Desempenho de {nome}` no\n * nível 1 e o nome da matéria no nível 2, os dois variáveis e sem teto de\n * tamanho, o que daria um nome de aba instável. Dentro do limite de 31 do Excel\n * (acima disso o arquivo INTEIRO não abre) e sem nenhum dos caracteres que o\n * formato proíbe (`: \\ / ? * [ ]`).\n */\nexport const SUMMARY_SHEET_NAME = 'Resumo do estudante';\n\n/**\n * Nome da aba do nível 1: 25 caracteres.\n *\n * \"componente\" sozinho, e não \"componente curricular\" como no resto da tela:\n * o nome por extenso daria 36 caracteres e estouraria o limite de 31 do Excel,\n * acima do qual o arquivo INTEIRO não abre. O cabeçalho da coluna dentro da\n * aba (`SUBJECTS_HEADERS`) continua com o termo completo.\n */\nexport const SUBJECTS_SHEET_NAME = 'Desempenho por componente';\n\n/**\n * Nome da aba do nível 2: 25 caracteres.\n *\n * \"habilidade\" e não \"conteúdo\", que é como o TIPO chama\n * (`StudentContentPerformanceItem`): a tela escreve \"Nenhuma habilidade\n * encontrada\" quando a lista vem vazia, então é essa a palavra que o produto\n * usa para este nível.\n */\nexport const CONTENTS_SHEET_NAME = 'Desempenho por habilidade';\n\n/** Célula sem valor — o código BNCC que a tela omite quando vem nulo. */\nconst EMPTY_CELL = '';\n\nconst SUMMARY_HEADERS = ['Indicador', 'Valor'];\n\n/**\n * Rótulos das linhas do resumo.\n *\n * Textos que a planilha acrescenta: no card da tela nome, escola, turma, média\n * e badge aparecem sem rótulo nenhum, e uma tabela de chave/valor precisa de\n * chave. Escola e turma saem em duas linhas, embora o card as junte em\n * `{school} - {class}`, porque duas colunas filtram do outro lado e o traço não\n * carrega informação.\n */\nconst SUBJECT_ROW_LABEL = 'Componente curricular';\nconst STUDENT_ROW_LABEL = 'Estudante';\nconst SCHOOL_ROW_LABEL = 'Escola';\nconst CLASS_ROW_LABEL = 'Turma';\nconst AVERAGE_ROW_LABEL = 'Média';\nconst PERFORMANCE_ROW_LABEL = 'Desempenho';\n\n/**\n * Colunas do nível 1, na ordem em que o `SubjectItem` as desenha: nome,\n * contagem de questões, barra de percentual.\n */\nconst SUBJECTS_HEADERS = ['Componente curricular', 'Questões', 'Acertos (%)'];\n\n/**\n * Colunas do nível 2, na ordem em que o `ContentItem` as desenha: nome, código\n * BNCC, contagem de questões, barra de percentual e a contagem de acertos que\n * fecha a linha (\"3 de 4\" — o total já é a coluna \"Questões\").\n */\nconst CONTENTS_HEADERS = [\n 'Habilidade',\n 'Código BNCC',\n 'Questões',\n 'Acertos (%)',\n 'Acertos',\n];\n\n/**\n * Percentual como a tela o arredonda, em célula NUMÉRICA — `75` e não `\"75%\"`.\n *\n * O \"%\" é formatação de exibição, e um número é o que permite ordenar e somar\n * do outro lado; é a mesma diferença deliberada que os dois modais irmãos já\n * adotam. `Math.round` é o que os dois lugares da tela aplicam: a média do card\n * passa por `formatPercentageRounded` (`utils/utils.ts:32-34`) e o percentual\n * de cada linha pela `ProgressBar`, que escreve `Math.round(percentage)`\n * (`ProgressBar.tsx:576`).\n *\n * O que NÃO é copiado da `ProgressBar` são as defesas de entrada — ela também\n * troca `NaN` por 0 e limita o valor a 0..100 antes de arredondar\n * (`ProgressBar.tsx:164-170`) —, porque `correctPercentage` e `average` chegam\n * como percentual já calculado pelo backend; um valor fora da faixa sairia\n * diferente aqui e na tela, e é o número cru que a planilha deve mostrar. É a\n * mesma escolha que o `StudentLessonProgressModal` fez diante do\n * `ProgressCircle`.\n */\nconst roundPercentage = (value: number): number => Math.round(value);\n\n/**\n * Aba de indicadores: uma linha por informação visível do cabeçalho, na ordem\n * da tela.\n *\n * A linha da matéria só existe quando a tela mostra o nome dela — isto é, no\n * nível 2, onde ela é o TÍTULO do modal, acima do card. Por isso vem primeiro.\n * No nível 1 o título é `Desempenho de {nome}`, que não acrescenta nada à linha\n * \"Estudante\" logo abaixo.\n *\n * @param data - O que o modal tem carregado; `null` nos estados de carregando,\n * erro e sem-dado, em que a tela não desenha o card e a aba sai só com\n * cabeçalho.\n * @param subjectName - O nome que o título do modal está exibindo, ou `null` no\n * nível 1. Vem do componente, não de `data`, porque é o estado local\n * `selectedSubject` que a tela renderiza.\n */\nexport function buildSummarySheet(\n data: StudentDetailsData | null,\n subjectName: string | null\n): SheetConfig {\n if (!data) {\n return { name: SUMMARY_SHEET_NAME, headers: SUMMARY_HEADERS, rows: [] };\n }\n\n const { student } = data;\n const rows: ExcelCell[][] = [];\n\n if (subjectName) {\n rows.push([SUBJECT_ROW_LABEL, subjectName]);\n }\n\n rows.push(\n [STUDENT_ROW_LABEL, student.name],\n [SCHOOL_ROW_LABEL, student.school],\n [CLASS_ROW_LABEL, student.class],\n [AVERAGE_ROW_LABEL, roundPercentage(student.average)],\n [\n PERFORMANCE_ROW_LABEL,\n SIMULATED_PERFORMANCE_TAG_CONFIG[student.performance].label,\n ]\n );\n\n return { name: SUMMARY_SHEET_NAME, headers: SUMMARY_HEADERS, rows };\n}\n\n/**\n * Aba da lista: a do nível que está aberto, uma linha por item.\n *\n * Sem dado carregado cai no nível 1, que é onde o modal sempre abre — o efeito\n * de abertura busca com `subjectId: null`\n * (`SimulatedStudentDetailsModal.tsx:43-48`).\n *\n * A aba existe mesmo com a lista vazia, embora a tela troque a lista por\n * \"Nenhuma matéria encontrada\" / \"Nenhuma habilidade encontrada\" nesse caso:\n * uma aba só com cabeçalho diz \"não havia itens\", enquanto um conjunto de abas\n * que muda de tamanho conforme o dado quebra quem consome o arquivo. A mensagem\n * em si fica de fora — ela é a ausência de linhas, e escrevê-la como linha\n * inventaria um item que não existe.\n */\nexport function buildDetailsSheet(\n data: StudentDetailsData | null\n): SheetConfig {\n if (data && !isStudentSubjectsData(data)) {\n const rows: ExcelCell[][] = data.contents.map((content) => [\n content.contentName,\n content.bnccCode || EMPTY_CELL,\n content.questionsCount,\n roundPercentage(content.performance.correctPercentage),\n content.performance.correct,\n ]);\n\n return { name: CONTENTS_SHEET_NAME, headers: CONTENTS_HEADERS, rows };\n }\n\n const rows: ExcelCell[][] = (data?.subjects ?? []).map((subject) => [\n subject.name,\n subject.questionsCount,\n roundPercentage(subject.performance.correctPercentage),\n ]);\n\n return { name: SUBJECTS_SHEET_NAME, headers: SUBJECTS_HEADERS, rows };\n}\n\n/**\n * As abas da planilha deste modal, na ordem em que os blocos aparecem na tela.\n *\n * São sempre DUAS: o resumo do cabeçalho e a lista do nível visível. A segunda\n * troca de nome e de colunas conforme o nível, porque uma aba chamada\n * \"Desempenho por matéria\" com habilidades dentro mentiria; o que não muda é a\n * quantidade.\n */\nexport function buildSimulatedStudentDetailsSheets(\n data: StudentDetailsData | null,\n subjectName: string | null\n): SheetConfig[] {\n return [buildSummarySheet(data, subjectName), buildDetailsSheet(data)];\n}\n","import type { OverviewAggregationType } from '../components/SimulatedStudentsOverview/types';\nimport { PROFILE_ROLES } from '../types/chat';\n\n/**\n * Get the overview aggregation type based on user profile\n *\n * - GENERAL_MANAGER: students (default)\n * - REGIONAL_MANAGER: municipalities (aggregated by city)\n * - UNIT_MANAGER: classes (aggregated by turma)\n * - TEACHER: students (default)\n *\n * @param profileName - The user's profile name\n * @returns The aggregation type to use\n */\nexport function getAggregationTypeByProfile(\n profileName: string | undefined\n): OverviewAggregationType {\n switch (profileName) {\n case PROFILE_ROLES.UNIT_MANAGER:\n return 'classes';\n case PROFILE_ROLES.REGIONAL_MANAGER:\n return 'municipalities';\n case PROFILE_ROLES.GENERAL_MANAGER:\n case PROFILE_ROLES.TEACHER:\n default:\n return 'students';\n }\n}\n\n/**\n * Check if the profile should use aggregated overview (classes or municipalities)\n *\n * @param profileName - The user's profile name\n * @returns True if the profile should use aggregated overview\n */\nexport function shouldUseAggregatedOverview(\n profileName: string | undefined\n): boolean {\n return (\n profileName === PROFILE_ROLES.UNIT_MANAGER ||\n profileName === PROFILE_ROLES.REGIONAL_MANAGER\n );\n}\n","import type { ColumnConfig } from '../TableProvider';\nimport type { StudentsHighlightPeriod } from '../../hooks/useStudentsHighlight';\nimport type { SimulatedFilters } from '../SimulatedFilters/types';\nimport type { ScoreType } from '../../types/common';\nimport type { GeneralOverviewData } from '../GeneralOverviewSection/types';\nimport type {\n SimulatedOverviewData,\n SimulatedStudentItem,\n AggregatedOverviewData,\n OverviewAggregationType,\n} from '../SimulatedStudentsOverview/types';\nimport type {\n ContentsPerformanceData,\n SimulatedContentItem,\n} from '../SimulatedContentsPerformance/types';\nimport type { BaseApiClient } from '../../types/api';\n\n/**\n * View tabs for simulated exams (students vs skills)\n */\nexport enum SimulatedViewTab {\n STUDENTS = 'students',\n SKILLS = 'skills',\n}\n\nexport interface UseSimulatedPerformanceOptions {\n api: BaseApiClient;\n /**\n * User's profile name to determine aggregation type.\n * - UNIT_MANAGER: aggregates by classes\n * - REGIONAL_MANAGER: aggregates by municipalities\n * - GENERAL_MANAGER, TEACHER, others: shows students (default)\n */\n profileName?: string;\n /**\n * When false, the hook will not make automatic API calls (effect-triggered fetches).\n * The initial data load and effect-based reloads (e.g., scoreType URL changes) will be\n * skipped. However, manual handler calls (handlePeriodChange, handleFiltersApply, etc.)\n * will still trigger API requests if invoked directly.\n * Useful for lazy loading when the simulated report tab is not active.\n * @default true\n */\n enabled?: boolean;\n}\n\nexport interface UseSimulatedPerformanceReturn {\n // === Query Params (gerenciados internamente) ===\n period: StudentsHighlightPeriod;\n scoreType: ScoreType;\n\n // === Estados de Seleção ===\n selectedAreaKnowledgeId: string | null;\n /** Related IDs for merged areas (includes all IDs when area was deduplicated) */\n selectedAreaRelatedIds: string[];\n selectedSubjectId: string | null;\n simulatedViewTab: SimulatedViewTab;\n isEssaySelected: boolean;\n\n // === Filtros ===\n filters: SimulatedFilters;\n activeFiltersCount: number;\n\n // === Aggregation Type ===\n aggregationType: OverviewAggregationType;\n\n // === Dados da API ===\n generalOverview: {\n data: GeneralOverviewData | null;\n loading: boolean;\n error: string | null;\n };\n /**\n * Aggregated overview data based on profile.\n * - students: contains StudentsOnlyOverviewData\n * - classes: contains ClassesOverviewData\n * - municipalities: contains MunicipalitiesOverviewData\n */\n aggregatedOverview: {\n data: AggregatedOverviewData | null;\n loading: boolean;\n isRefreshing: boolean;\n error: string | null;\n };\n /**\n * Paginated students list for the table (legacy endpoint).\n * Still used for drilling into individual student details.\n */\n studentsOverview: {\n data: SimulatedOverviewData | null;\n loading: boolean;\n isRefreshing: boolean;\n error: string | null;\n };\n contentsPerformance: {\n data: ContentsPerformanceData | null;\n loading: boolean;\n isRefreshing: boolean;\n error: string | null;\n };\n\n // === Handlers ===\n handlePeriodChange: (period: string) => void;\n handleScoreTypeChange: (scoreType: string) => void;\n handleAreaKnowledgeChange: (areaId: string | null) => void;\n handleSubjectChange: (subjectId: string | null) => void;\n handleViewTabChange: (tab: string) => void;\n handleFiltersApply: (filters: SimulatedFilters) => void;\n handleStudentsParamsChange: (params: Record<string, unknown>) => void;\n handleContentsParamsChange: (params: Record<string, unknown>) => void;\n handleStudentRowClick: (row: SimulatedStudentItem) => void;\n handleContentRowClick: (row: SimulatedContentItem) => void;\n\n // === Modais ===\n studentModal: {\n isOpen: boolean;\n student: { userInstitutionId: string; name: string } | null;\n close: () => void;\n };\n contentModal: {\n isOpen: boolean;\n content: { contentId: string; contentName: string } | null;\n close: () => void;\n };\n filtersModal: {\n isOpen: boolean;\n open: () => void;\n close: () => void;\n };\n\n // === Colunas das Tabelas ===\n studentsTableColumns: ColumnConfig<SimulatedStudentItem>[];\n contentsTableColumns: ColumnConfig<SimulatedContentItem>[];\n}\n\nexport interface SimulatedPerformanceViewProps extends UseSimulatedPerformanceReturn {\n api: BaseApiClient;\n noSearchImage?: string;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-Y4IGIWP4.js","../src/components/SimulatedPerformance/useSimulatedPerformance.tsx","../src/components/GeneralOverviewSection/GeneralOverviewSection.tsx","../src/components/GeneralOverviewSection/useGeneralOverview.ts","../src/components/SimulatedStudentsOverview/useSimulatedOverview.ts","../src/components/SimulatedStudentDetailsModal/types.ts","../src/components/SimulatedStudentsOverview/utils.ts","../src/components/SimulatedStudentsOverview/useAggregatedOverview.ts","../src/components/SimulatedContentsPerformance/useSimulatedContents.ts","../src/components/AreaKnowledgeSelector/AreaKnowledgeSelector.tsx","../src/components/AreaKnowledgeSelector/types.ts","../src/components/SimulatedStudentDetailsModal/SimulatedStudentDetailsModal.tsx","../src/components/SimulatedStudentDetailsModal/useSimulatedStudentDetails.ts","../src/components/SimulatedStudentDetailsModal/utils.ts","../src/components/SimulatedStudentDetailsModal/exportSheets.ts","../src/utils/profileAggregation.ts","../src/components/SimulatedPerformance/types.ts"],"names":["useCallback","PerformanceBadgeAction","ReportSimulationType","endpoint","useState","initialState","buildEndpoint","jsx","jsxs","SimulatedViewTab","useMemo","useRef"],"mappings":"AAAA;AACE;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACE;AACA;AACA;AACA;AACA;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACE;AACA;AACA;AACA;AACF,sDAA4B;AAC5B;AACA;ACzCA,8BAAkE;AAClE,kDAAgC;AD2ChC;AACA;AEnBI,+CAAA;AAVJ,SAAS,QAAA,CAAS;AAAA,EAChB,IAAA;AAAA,EACA,UAAA,EAAA;AACF,CAAA,EAGG;AACD,EAAA,MAAM,QAAA,EAAU,wCAAA,IAAU,CAAK,KAAA,EAAO,GAAG,CAAA;AAEzC,EAAA,uBACE,8BAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAU,yEAAA;AAAA,MACV,KAAA,EAAO,EAAE,eAAA,EAAiB,QAAQ,CAAA;AAAA,MAGlC,QAAA,EAAA;AAAA,wBAAA,6BAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAU,4DAAA;AAAA,YACV,KAAA,EAAO,EAAE,eAAA,EAAiB,IAAA,CAAK,MAAM,CAAA;AAAA,YAErC,QAAA,kBAAA,6BAAA,mCAAC,EAAA,EAAW,QAAA,EAAU,IAAA,CAAK,IAAA,EAAM,IAAA,EAAM,EAAA,EAAI,KAAA,EAAM,QAAA,CAAQ;AAAA,UAAA;AAAA,QAC3D,CAAA;AAAA,wBAGA,6BAAA;AAAA,UAAC,6BAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAK,IAAA;AAAA,YACL,MAAA,EAAO,UAAA;AAAA,YACP,SAAA,EAAU,uDAAA;AAAA,YAET,QAAA,EAAA,IAAA,CAAK;AAAA,UAAA;AAAA,QACR,CAAA;AAAA,wBAGA,6BAAA,6BAAC,EAAA,EAAK,IAAA,EAAK,IAAA,EAAK,MAAA,EAAO,MAAA,EAAO,SAAA,EAAU,eAAA,EACrC,QAAA,EAAA,0CAAA,IAAY,CAAK,UAAA,EAAY,SAAS,EAAA,CACzC;AAAA,MAAA;AAAA,IAAA;AAAA,EACF,CAAA;AAEJ;AAOA,SAAS,SAAA,CAAU,EAAE,MAAM,CAAA,EAAyC;AAClE,EAAA,MAAM,QAAA,EAAU,wCAAA,KAAU,CAAM,KAAA,EAAO,GAAG,CAAA;AAE1C,EAAA,uBACE,8BAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAU,yEAAA;AAAA,MACV,KAAA,EAAO,EAAE,eAAA,EAAiB,QAAQ,CAAA;AAAA,MAGlC,QAAA,EAAA;AAAA,wBAAA,6BAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAU,4DAAA;AAAA,YACV,KAAA,EAAO,EAAE,eAAA,EAAiB,KAAA,CAAM,MAAM,CAAA;AAAA,YAEtC,QAAA,kBAAA,6BAAA,mCAAC,EAAA,EAAW,QAAA,EAAU,KAAA,CAAM,IAAA,EAAM,IAAA,EAAM,EAAA,EAAI,KAAA,EAAM,QAAA,CAAQ;AAAA,UAAA;AAAA,QAC5D,CAAA;AAAA,wBAGA,6BAAA;AAAA,UAAC,6BAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAK,IAAA;AAAA,YACL,MAAA,EAAO,UAAA;AAAA,YACP,SAAA,EAAU,uDAAA;AAAA,YAET,QAAA,EAAA,KAAA,CAAM;AAAA,UAAA;AAAA,QACT,CAAA;AAAA,wBAGA,6BAAA,6BAAC,EAAA,EAAK,IAAA,EAAK,IAAA,EAAK,MAAA,EAAO,MAAA,EAAO,SAAA,EAAU,eAAA,EACrC,QAAA,EAAA,0CAAA,KAAY,CAAM,UAAA,EAAA,6BAAgC,EAAA,CACrD;AAAA,MAAA;AAAA,IAAA;AAAA,EACF,CAAA;AAEJ;AAKA,SAAS,gBAAA,CAAA,EAAmB;AAC1B,EAAA,uBACE,8BAAA,KAAC,EAAA,EAAI,SAAA,EAAU,0FAAA,EACb,QAAA,EAAA;AAAA,oBAAA,6BAAA,gCAAC,EAAA,EAAgB,SAAA,EAAU,4BAAA,CAA4B,CAAA;AAAA,oBACvD,6BAAA,gCAAC,EAAA,EAAgB,SAAA,EAAU,gBAAA,CAAgB,CAAA;AAAA,oBAC3C,6BAAA,gCAAC,EAAA,EAAgB,SAAA,EAAU,WAAA,CAAW;AAAA,EAAA,EAAA,CACxC,CAAA;AAEJ;AAsBO,SAAS,sBAAA,CAAuB;AAAA,EACrC,IAAA;AAAA,EACA,QAAA,EAAU,KAAA;AAAA,EACV,MAAA,EAAQ,IAAA;AAAA,EACR,UAAA,EAAA;AACF,CAAA,EAA0C;AACxC,EAAA,GAAA,CAAI,KAAA,EAAO;AACT,IAAA,uBACE,6BAAA,KAAC,EAAA,EAAI,SAAA,EAAU,oDAAA,EACb,QAAA,kBAAA,6BAAA,6BAAC,EAAA,EAAK,IAAA,EAAK,IAAA,EAAK,SAAA,EAAU,gBAAA,EACvB,QAAA,EAAA,MAAA,CACH,EAAA,CACF,CAAA;AAAA,EAEJ;AAEA,EAAA,GAAA,CAAI,OAAA,EAAS;AACX,IAAA,uBACE,8BAAA,KAAC,EAAA,EAAI,SAAA,EAAU,gEAAA,EAEb,QAAA,EAAA;AAAA,sBAAA,8BAAA,KAAC,EAAA,EAAI,SAAA,EAAU,WAAA,EACb,QAAA,EAAA;AAAA,wBAAA,6BAAA,6BAAC,EAAA,EAAK,IAAA,EAAK,IAAA,EAAK,MAAA,EAAO,MAAA,EAAO,SAAA,EAAU,eAAA,EAAgB,QAAA,EAAA,QAAA,CAExD,CAAA;AAAA,wBACA,6BAAA,gCAAC,EAAA,EAAgB,SAAA,EAAU,YAAA,CAAY;AAAA,MAAA,EAAA,CACzC,CAAA;AAAA,sBAGA,8BAAA,KAAC,EAAA,EAAI,SAAA,EAAU,yBAAA,EACb,QAAA,EAAA;AAAA,wBAAA,6BAAA,KAAC,EAAA,EAAI,SAAA,EAAU,QAAA,EACb,QAAA,kBAAA,6BAAA,gCAAC,EAAA,EAAgB,SAAA,EAAU,0BAAA,CAA0B,EAAA,CACvD,CAAA;AAAA,wBACA,6BAAA,gCAAC,EAAA,EAAgB,SAAA,EAAU,WAAA,CAAW;AAAA,MAAA,EAAA,CACxC,CAAA;AAAA,sBAGA,6BAAA,KAAC,EAAA,EAAI,SAAA,EAAU,YAAA,EACZ,QAAA,EAAA,CAAC,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,EAAA,mBACpB,6BAAA,gBAAC,EAAA,CAAA,CAAA,EAAsB,CAAG,CAC3B,EAAA,CACH;AAAA,IAAA,EAAA,CACF,CAAA;AAAA,EAEJ;AAEA,EAAA,GAAA,CAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,uBACE,8BAAA,KAAC,EAAA,EAAI,SAAA,EAAU,gEAAA,EAEb,QAAA,EAAA;AAAA,oBAAA,8BAAA,KAAC,EAAA,EAAI,SAAA,EAAU,WAAA,EACb,QAAA,EAAA;AAAA,sBAAA,6BAAA,6BAAC,EAAA,EAAK,IAAA,EAAK,IAAA,EAAK,MAAA,EAAO,MAAA,EAAO,SAAA,EAAU,eAAA,EAAgB,QAAA,EAAA,QAAA,CAExD,CAAA;AAAA,sBACA,6BAAA,6BAAC,EAAA,EAAK,IAAA,EAAK,IAAA,EAAK,SAAA,EAAU,eAAA,EAAgB,QAAA,EAAA,2HAAA,CAG1C;AAAA,IAAA,EAAA,CACF,CAAA;AAAA,oBAGA,8BAAA,KAAC,EAAA,EAAI,SAAA,EAAU,yBAAA,EACb,QAAA,EAAA;AAAA,sBAAA,6BAAA,KAAC,EAAA,EAAI,SAAA,EAAU,QAAA,EACb,QAAA,kBAAA,6BAAA;AAAA,QAAC,oCAAA;AAAA,QAAA;AAAA,UACC,KAAA,EACE,UAAA,IAAA,gBAAA,EACI,IAAA,CAAK,kBAAA,EAAoB,GAAA,EACzB,IAAA,CAAK,iBAAA;AAAA,UAEX,OAAA,EAAQ,OAAA;AAAA,UACR,IAAA,EAAK;AAAA,QAAA;AAAA,MACP,EAAA,CACF,CAAA;AAAA,sBACA,6BAAA;AAAA,QAAC,6BAAA;AAAA,QAAA;AAAA,UACC,IAAA,EAAK,IAAA;AAAA,UACL,MAAA,EAAO,MAAA;AAAA,UACP,SAAA,EAAU,uCAAA;AAAA,UAET,QAAA,EAAA,0CAAA,IAAY,CAAK,iBAAA,EAAmB,SAAS;AAAA,QAAA;AAAA,MAChD;AAAA,IAAA,EAAA,CACF,CAAA;AAAA,IAAA,CAGE,IAAA,CAAK,KAAA,CAAM,OAAA,EAAS,EAAA,GAAK,IAAA,CAAK,KAAA,EAAA,mBAC9B,8BAAA,KAAC,EAAA,EAAI,SAAA,EAAU,YAAA,EACZ,QAAA,EAAA;AAAA,MAAA,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,EAAA,mBACf,6BAAA,QAAC,EAAA,EAAuB,IAAA,EAAY,UAAA,CAAA,EAArB,IAAA,CAAK,EAAsC,CAC3D,CAAA;AAAA,MACA,IAAA,CAAK,MAAA,mBAAS,6BAAA,SAAC,EAAA,EAAU,KAAA,EAAO,IAAA,CAAK,MAAA,CAAO;AAAA,IAAA,EAAA,CAC/C;AAAA,EAAA,EAAA,CAEJ,CAAA;AAEJ;AFjDA;AACA;AG7KA;AAYA,IAAM,aAAA,EAAwC;AAAA,EAC5C,IAAA,EAAM,IAAA;AAAA,EACN,OAAA,EAAS,KAAA;AAAA,EACT,YAAA,EAAc,KAAA;AAAA,EACd,KAAA,EAAO;AACT,CAAA;AA0BO,SAAS,kBAAA,CACd,GAAA,EAC0B;AAC1B,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,EAAA,EAAI,6BAAA,YAA8C,CAAA;AACxE,EAAA,MAAM,aAAA,EAAe,2BAAA,CAAQ,CAAA;AAO7B,EAAA,MAAM,cAAA,EAAgB,gCAAA;AAAA,IACpB,MAAA,CAAO,MAAA,EAA+B,QAAA,EAAU,KAAA,EAAA,GAAU;AACxD,MAAA,MAAM,iBAAA,EAAmB,EAAE,YAAA,CAAa,OAAA;AAGxC,MAAA,QAAA,CAAS,CAAC,IAAA,EAAA,GAAA,CAAU;AAAA,QAClB,GAAG,IAAA;AAAA,QACH,OAAA,EAAS,CAAC,OAAA;AAAA,QACV,YAAA,EAAc,OAAA;AAAA,QACd,KAAA,EAAO;AAAA,MACT,CAAA,CAAE,CAAA;AAEF,MAAA,IAAI;AAEF,QAAA,IAAI,IAAA,EAAM,yCAAA;AACV,QAAA,GAAA,CAAI,MAAA,CAAO,UAAA,GAAa,MAAA,CAAO,UAAA,IAAc,YAAA,EAAc;AACzD,UAAA,IAAA,GAAO,CAAA,WAAA,EAAc,MAAA,CAAO,SAAS,CAAA,CAAA;AACvC,QAAA;AAGa,QAAA;AACI,UAAA;AACiB,UAAA;AACV,UAAA;AACQ,UAAA;AAChC,QAAA;AAEiE,QAAA;AAG3B,QAAA;AAE7B,QAAA;AACa,UAAA;AACX,UAAA;AACK,UAAA;AACP,UAAA;AACR,QAAA;AACW,MAAA;AAE0B,QAAA;AAGrB,QAAA;AAIH,QAAA;AAEM,QAAA;AACf,UAAA;AACM,UAAA;AACK,UAAA;AACP,UAAA;AACP,QAAA;AACJ,MAAA;AACF,IAAA;AACI,IAAA;AACN,EAAA;AAKgC,EAAA;AAEjB,IAAA;AACQ,IAAA;AAClB,EAAA;AAEE,EAAA;AACF,IAAA;AACH,IAAA;AACA,IAAA;AACF,EAAA;AACF;AH4GgD;AACA;AI7O7BA;AJ+O6B;AACA;AK5M5C;AACmC,EAAA;AAC5B,IAAA;AACE,IAAA;AACX,EAAA;AACyC,EAAA;AAChC,IAAA;AACE,IAAA;AACX,EAAA;AACyC,EAAA;AAChC,IAAA;AACE,IAAA;AACX,EAAA;AAC2C,EAAA;AAClC,IAAA;AACE,IAAA;AACX,EAAA;AACF;AAKYC;AACA,EAAA;AACH,EAAA;AACG,EAAA;AACF,EAAA;AAJEA,EAAAA;AAAA;AAaR;AACmC,EAAA;AACI,EAAA;AACA,EAAA;AACE,EAAA;AAC7C;AAKYC;AACD,EAAA;AACA,EAAA;AACA,EAAA;AAHCA,EAAAA;AAAA;AA4FmB;AACR,EAAA;AACvB;AAO+B;AACR,EAAA;AACvB;AAeE;AAEwB,EAAA;AACjB,IAAA;AACI,MAAA;AACa,QAAA;AACO,QAAA;AAC3B,MAAA;AACG,IAAA;AACI,MAAA;AACa,QAAA;AACO,QAAA;AAC3B,MAAA;AACF,IAAA;AACU,MAAA;AACZ,EAAA;AACF;ALqFgD;AACA;AM9R+B;AACjC,EAAA;AAC9C;AASoC;AAGvB,EAAA;AAIb;AAS+B;AACiB,EAAA;AAChD;AASsC;AAEI,EAAA;AAE1C;ANiQgD;AACA;AItR9C;AAGiC,EAAA;AACI,IAAA;AACJ,IAAA;AACO,MAAA;AACtC,IAAA;AACoC,IAAA;AACnB,IAAA;AAEC,IAAA;AACTC,MAAAA;AACT,IAAA;AAEiC,IAAA;AACnC,EAAA;AAEwB,EAAA;AACW,EAAA;AAEU,kBAAA;AAEJ,kBAAA;AAGI,EAAA;AACP,IAAA;AACtC,EAAA;AAEoC,EAAA;AACnB,EAAA;AAEC,EAAA;AACT,IAAA;AACT,EAAA;AAEiC,EAAA;AACnC;AAKgD;AACxC,EAAA;AACG,EAAA;AACK,EAAA;AACP,EAAA;AACT;AAuB8B;AACJC,EAAAA;AACT,IAAA;AACf,EAAA;AACuCC,EAAAA;AACCD,EAAAA;AACUC,EAAAA;AAGrB,EAAA;AAOD,EAAA;AACnB,IAAA;AACU,MAAA;AACG,MAAA;AACM,MAAA;AACN,MAAA;AACI,MAAA;AACL,MAAA;AACG,MAAA;AACC,MAAA;AACE,MAAA;AACI,MAAA;AACJ,MAAA;AACzB,IAAA;AACF,EAAA;AAEsBL,EAAAA;AACwC,IAAA;AAClB,MAAA;AAEpC,MAAA;AACW,QAAA;AACS,UAAA;AACf,QAAA;AACU,UAAA;AACjB,QAAA;AACa,QAAA;AAEyB,QAAA;AACN,QAAA;AAEL,QAAA;AACzB,UAAA;AACA,UAAA;AACF,QAAA;AAGsC,QAAA;AAEZ,QAAA;AACd,MAAA;AAE0B,QAAA;AAEtC,QAAA;AACE,UAAA;AACE,YAAA;AACA,YAAA;AACF,UAAA;AACF,QAAA;AACA,MAAA;AAEsC,QAAA;AACpB,UAAA;AACK,UAAA;AACvB,QAAA;AACF,MAAA;AACF,IAAA;AACI,IAAA;AACN,EAAA;AAEgC,EAAA;AAEjB,IAAA;AACY,IAAA;AACM,IAAA;AACU,IAAA;AACd,IAAA;AACxB,EAAA;AAEwC,EAAA;AAC/C;AJ6NgD;AACA;AOja7BA;AAoCjB;AAIiC,EAAA;AACxB,IAAA;AACT,EAAA;AAEwB,EAAA;AACW,EAAA;AAGF,EAAA;AAEY,kBAAA;AACJ,kBAAA;AAEI,EAAA;AACP,IAAA;AACtC,EAAA;AAEiB,EAAA;AACsB,EAAA;AACzC;AAKiD;AACzC,EAAA;AACG,EAAA;AACK,EAAA;AACP,EAAA;AACT;AAoC+B;AACLI,EAAAA;AACT,IAAA;AACf,EAAA;AACuCC,EAAAA;AACCD,EAAAA;AACUC,EAAAA;AAGrB,EAAA;AAOA,EAAA;AACpB,IAAA;AACU,MAAA;AACG,MAAA;AACM,MAAA;AACN,MAAA;AACI,MAAA;AACL,MAAA;AACG,MAAA;AACtB,IAAA;AACF,EAAA;AAEsBL,EAAAA;AACyC,IAAA;AACnB,MAAA;AAEpC,MAAA;AACW,QAAA;AACS,UAAA;AACf,QAAA;AACU,UAAA;AACjB,QAAA;AACa,QAAA;AAEIM,QAAAA;AACR,UAAA;AACA,UAAA;AACA,UAAA;AACT,QAAA;AACgC,QAAA;AAE5B,QAAA;AAE4B,QAAA;AACd,UAAA;AACa,YAAA;AACzB,cAAA;AACA,cAAA;AACF,YAAA;AAC6B,YAAA;AAC7B,YAAA;AACF,UAAA;AACuB,UAAA;AACM,YAAA;AACzB,cAAA;AACA,cAAA;AACF,YAAA;AAC6B,YAAA;AAC7B,YAAA;AACF,UAAA;AACK,UAAA;AACI,UAAA;AACoB,YAAA;AACzB,cAAA;AACA,cAAA;AACF,YAAA;AAC6B,YAAA;AAC7B,YAAA;AACF,UAAA;AACF,QAAA;AAGsC,QAAA;AAElB,QAAA;AACR,MAAA;AAE0B,QAAA;AAEtC,QAAA;AACuB,UAAA;AACvB,QAAA;AACA,MAAA;AAEsC,QAAA;AACpB,UAAA;AACK,UAAA;AACvB,QAAA;AACF,MAAA;AACF,IAAA;AACI,IAAA;AACN,EAAA;AAEgC,EAAA;AAEjB,IAAA;AACY,IAAA;AACM,IAAA;AACU,IAAA;AACd,IAAA;AACxB,EAAA;AAEwC,EAAA;AAC/C;AP0TgD;AACA;AQjhB7BN;AAmBjB;AAGoD,EAAA;AAC3C,IAAA;AACT,EAAA;AAEwB,EAAA;AACW,EAAA;AAEU,kBAAA;AAEJ,kBAAA;AAEI,EAAA;AACP,IAAA;AACtC,EAAA;AAEoC,EAAA;AACnB,EAAA;AAEC,EAAA;AACT,IAAA;AACT,EAAA;AAEiC,EAAA;AACnC;AAuB8B;AACyC,EAAA;AACzB,EAAA;AACJI,EAAAA;AACc,EAAA;AACvB,EAAA;AACC,EAAA;AAE9B,EAAA;AAE6D,EAAA;AAC9C,IAAA;AAEc,IAAA;AAID,IAAA;AAGV,IAAA;AACI,IAAA;AACL,IAAA;AACG,IAAA;AACC,IAAA;AACE,IAAA;AACI,IAAA;AACJ,IAAA;AACzB,EAAA;AAEgD,EAAA;AACZ,IAAA;AAChC,MAAA;AACF,IAAA;AACY,IAAA;AACC,IAAA;AACG,IAAA;AACK,IAAA;AACC,IAAA;AACxB,EAAA;AAE8C,EAAA;AACV,IAAA;AAChC,MAAA;AACF,IAAA;AACqC,IAAA;AACpB,MAAA;AACV,IAAA;AACe,MAAA;AACtB,IAAA;AACa,IAAA;AACf,EAAA;AAIE,EAAA;AAEkC,IAAA;AAChC,MAAA;AACF,IAAA;AAC0B,IAAA;AACJ,IAAA;AACxB,EAAA;AAEyD,EAAA;AACrB,IAAA;AAChC,MAAA;AACF,IAAA;AAGM,IAAA;AAEe,IAAA;AACT,IAAA;AACd,EAAA;AAEkD,EAAA;AACd,IAAA;AAChC,MAAA;AACF,IAAA;AACgB,IAAA;AACK,IAAA;AACvB,EAAA;AAEsBJ,EAAAA;AACwC,IAAA;AACjB,MAAA;AACH,MAAA;AAGvB,MAAA;AACkB,QAAA;AAC/B,QAAA;AACF,MAAA;AAEyC,MAAA;AAErC,MAAA;AACyB,QAAA;AACzB,UAAA;AACuB,UAAA;AACzB,QAAA;AACsC,QAAA;AAC1B,MAAA;AACmB,QAAA;AAC/B,MAAA;AACiC,QAAA;AACnC,MAAA;AACF,IAAA;AACI,IAAA;AACN,EAAA;AAEgC,EAAA;AAClB,IAAA;AACI,IAAA;AACK,IAAA;AACR,IAAA;AACS,IAAA;AACnB,EAAA;AAEE,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;ARwcgD;AACA;AS7oBxB;AASK;ATuoBmB;AACA;AU3oBnB;AV6oBmB;AACA;AS9jB1C;AAjDgC;AACpC,EAAA;AACA,EAAA;AACA,EAAA;AACU,EAAA;AACF,EAAA;AACO,EAAA;AACc;AAEuB,EAAA;AAChB,IAAA;AAC5B,MAAA;AACE,MAAA;AACR,IAAA;AAE0C,IAAA;AACH,MAAA;AAC1B,QAAA;AACE,QAAA;AACC,QAAA;AACd,MAAA;AACF,IAAA;AAEsC,IAAA;AAEpB,IAAA;AACoB,MAAA;AAC9B,QAAA;AACE,QAAA;AACR,MAAA;AACsB,MAAA;AACxB,IAAA;AAEO,IAAA;AACe,EAAA;AAGqB,EAAA;AACtB,IAAA;AACF,MAAA;AACZ,IAAA;AACa,MAAA;AACpB,IAAA;AACF,EAAA;AAEyC,EAAA;AAGlC,EAAA;AACQ,oBAAA;AAGI,oBAAA;AACZ,MAAA;AAAA,MAAA;AACc,QAAA;AACN,QAAA;AACQ,QAAA;AAEf,QAAA;AAAAO,0BAAAA;AAAC,YAAA;AAAA,YAAA;AACyB,cAAA;AACd,cAAA;AAET,cAAA;AAA6C,YAAA;AAChD,UAAA;AAEG,0BAAA;AACgB,YAAA;AAEQ,YAAA;AAElB,cAAA;AACE,gBAAA;AAAA,gBAAA;AACO,kBAAA;AACC,kBAAA;AACG,kBAAA;AAAA,gBAAA;AAEd,cAAA;AAEmB,YAAA;AAEnBA,cAAAA;AAAC,gBAAA;AAAA,gBAAA;AACW,kBAAA;AACmB,kBAAA;AAE7BA,kBAAAA;AAAC,oBAAA;AAAA,oBAAA;AACW,sBAAA;AACgB,sBAAA;AAAW,oBAAA;AACvC,kBAAA;AAAA,gBAAA;AACF,cAAA;AAEJ,YAAA;AAGG,YAAA;AAEI,cAAA;AACK,8BAAA;AAEV,YAAA;AAGN,UAAA;AAAA,QAAA;AAAA,MAAA;AAEJ,IAAA;AACF,EAAA;AAEJ;ATmmBgD;AACA;AWnvBlB;AAKA;AACC;AXivBiB;AACA;AYxvB7BP;AZ0vB6B;AACA;Aa3uB+B;AACtE,EAAA;AACqB,IAAA;AACX,IAAA;AACgB,IAAA;AACV,IAAA;AACE,IAAA;AACzB,EAAA;AACF;Ab6uBgD;AACA;AYtvBe;AACT,EAAA;AAC3C,IAAA;AACT,EAAA;AAEwB,EAAA;AACW,EAAA;AAEU,kBAAA;AAEJ,kBAAA;AAEL,EAAA;AACnB,EAAA;AAEC,EAAA;AACT,IAAA;AACT,EAAA;AAEiC,EAAA;AACnC;AA8BoC;AAC8B,EAAA;AACpB,EAAA;AACU,EAAA;AAGzB,EAAA;AAERA,EAAAA;AACqB,IAAA;AACE,MAAA;AAEpC,MAAA;AACa,QAAA;AACF,QAAA;AACD,QAAA;AAE0B,QAAA;AACN,QAAA;AAEL,QAAA;AACzB,UAAA;AACA,UAAA;AACF,QAAA;AAGsC,QAAA;AAEZ,QAAA;AACd,MAAA;AAE0B,QAAA;AAGrB,QAAA;AAGL,QAAA;AACS,QAAA;AACrB,MAAA;AAEsC,QAAA;AACpB,UAAA;AAClB,QAAA;AACF,MAAA;AACF,IAAA;AACI,IAAA;AACN,EAAA;AAEgC,EAAA;AAEjB,IAAA;AACD,IAAA;AACI,IAAA;AACH,IAAA;AACV,EAAA;AAEwC,EAAA;AAC/C;AZmsBgD;AACA;AcxvBd;AAUC;AAUA;AAGhB;AAE0B;AAWnB;AACA;AACD;AACD;AACE;AACI;AAMJ;AAOD;AACvB,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACF;AAoB8D;AAqB/C;AACF,EAAA;AACmC,IAAA;AAC9C,EAAA;AAEoB,EAAA;AACS,EAAA;AAEZ,EAAA;AAC2B,IAAA;AAC5C,EAAA;AAEK,EAAA;AAC6B,IAAA;AACC,IAAA;AACF,IAAA;AACa,IAAA;AAC5C,IAAA;AACE,MAAA;AACyC,MAAA;AAC3C,IAAA;AACF,EAAA;AAE4C,EAAA;AAC9C;AAkBe;AAC6B,EAAA;AACO,IAAA;AACrC,MAAA;AACY,MAAA;AACZ,MAAA;AAC4B,MAAA;AAChB,MAAA;AACrB,IAAA;AAEmC,IAAA;AACtC,EAAA;AAEwD,EAAA;AAC9C,IAAA;AACA,IAAA;AAC4B,IAAA;AACrC,EAAA;AAE4C,EAAA;AAC/C;AAWE;AAG8C,EAAA;AAChD;AdqoBgD;AACA;AWrvB5C;AA3GuB;AA0BkB;AAC3C,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACoC;AACQ,EAAA;AAEEI,EAAAA;AAM9B,EAAA;AACmB,IAAA;AAClB,MAAA;AACX,QAAA;AACA,QAAA;AACA,QAAA;AACW,QAAA;AACZ,MAAA;AACH,IAAA;AAC6B,EAAA;AAGf,EAAA;AACD,IAAA;AACL,MAAA;AACiB,MAAA;AACzB,IAAA;AACgB,EAAA;AAGSJ,EAAAA;AACY,IAAA;AACX,MAAA;AAEa,MAAA;AACxB,MAAA;AACX,QAAA;AACA,QAAA;AACA,QAAA;AACmB,QAAA;AACpB,MAAA;AACH,IAAA;AAC4C,IAAA;AAC9C,EAAA;AAGqC,EAAA;AACX,IAAA;AAED,IAAA;AACV,IAAA;AACX,MAAA;AACA,MAAA;AACA,MAAA;AACW,MAAA;AACZ,IAAA;AACoC,EAAA;AAKM,EAAA;AAEH,EAAA;AAEF,EAAA;AACtC,IAAA;AACE,MAAA;AACyC,MAAA;AAC3C,IAAA;AACsC,EAAA;AAItC,EAAA;AAMEO,oBAAAA;AAAC,MAAA;AAAA,MAAA;AACU,QAAA;AACD,QAAA;AACE,QAAA;AACC,QAAA;AACI,QAAA;AAEd,QAAA;AAAkD,MAAA;AACrD,IAAA;AACO,oBAAA;AAGuB,EAAA;AAGe,EAAA;AAUjC,EAAA;AACsB,IAAA;AACD,MAAA;AACF,QAAA;AAC1B,UAAA;AAAA,UAAA;AAEC,YAAA;AACkC,YAAA;AACnB,YAAA;AAAA,UAAA;AAHF,UAAA;AAKhB,QAAA;AACH,MAAA;AAGG,MAAA;AAIL,IAAA;AAEiC,IAAA;AAE7B,MAAA;AAAC,QAAA;AAAA,QAAA;AAEC,UAAA;AACe,UAAA;AACP,UAAA;AAAA,QAAA;AAHK,QAAA;AAKhB,MAAA;AACH,IAAA;AAGQ,IAAA;AAIV,EAAA;AAK4C,EAAA;AAC7B,IAAA;AACgB,MAAA;AAC7B,IAAA;AAEW,IAAA;AACyB,MAAA;AACpC,IAAA;AAEW,IAAA;AACkB,MAAA;AAC7B,IAAA;AAGO,IAAA;AAEY,sBAAA;AACR,wBAAA;AACG,0BAAA;AAGA,0BAAA;AACU,YAAA;AAAO,YAAA;AAAiB,YAAA;AACxC,UAAA;AACF,QAAA;AACK,wBAAA;AACG,0BAAA;AAGNA,0BAAAA;AAAC,YAAA;AAAA,YAAA;AACS,cAAA;AACA,cAAA;AACH,cAAA;AAEJ,cAAA;AAA2D,YAAA;AAC9D,UAAA;AACF,QAAA;AACF,MAAA;AAGe,sBAAA;AAGjB,IAAA;AAEJ,EAAA;AAGEA,EAAAA;AAAC,IAAA;AAAA,IAAA;AACC,MAAA;AACA,MAAA;AACO,MAAA;AACF,MAAA;AAKL,MAAA;AACiB,MAAA;AAEG,MAAA;AAAA,IAAA;AACtB,EAAA;AAEJ;AAKqB;AACnB,EAAA;AACA,EAAA;AACA,EAAA;AAKE;AAEAC,EAAAA;AAAC,IAAA;AAAA,IAAA;AACC,MAAA;AACU,MAAA;AAOE,MAAA;AAGX,MAAA;AACCD,QAAAA;AAAC,UAAA;AAAA,UAAA;AACW,YAAA;AACwB,YAAA;AAAM,UAAA;AAC1C,QAAA;AAIG,wBAAA;AACG,0BAAA;AAGA,0BAAA;AACK,YAAA;AAAe,YAAA;AAAE,YAAA;AAC5B,UAAA;AACF,QAAA;AAGK,wBAAA;AACF,UAAA;AAAA,UAAA;AAC4B,YAAA;AACnB,YAAA;AACH,YAAA;AACS,YAAA;AAAA,UAAA;AAElB,QAAA;AAGgB,wBAAA;AAAkD,MAAA;AAAA,IAAA;AACpE,EAAA;AAEJ;AAKqB;AACnB,EAAA;AACA,EAAA;AACA,EAAA;AAKE;AAEK,EAAA;AAEY,oBAAA;AACP,sBAAA;AAGS,sBAAA;AAEXA,QAAAA;AAII,wBAAA;AACK,UAAA;AAAe,UAAA;AAAE,UAAA;AAC5B,QAAA;AACF,MAAA;AACF,IAAA;AAGe,oBAAA;AACZ,MAAA;AAAA,MAAA;AAC4B,QAAA;AACnB,QAAA;AACH,QAAA;AACS,QAAA;AAAA,MAAA;AAElB,IAAA;AAGe,oBAAA;AACP,sBAAA;AAGA,sBAAA;AACH,QAAA;AAAQ,QAAA;AAAU,QAAA;AACrB,MAAA;AACF,IAAA;AACF,EAAA;AAEJ;AXouBgD;AACA;AexlC9C;AAEqB,EAAA;AACnB,IAAA;AACS,MAAA;AACT,IAAA;AACS,MAAA;AACT,IAAA;AACA,IAAA;AACA,IAAA;AACS,MAAA;AACX,EAAA;AACF;AASE;AAGE,EAAA;AAGJ;Af6kCgD;AACA;AgBpmCpCE;AACC,EAAA;AACF,EAAA;AAFCA,EAAAA;AAAA;AhB0mCoC;AACA;AC3lC5C;AARiC;AACnC,EAAA;AAGC;AACiB,EAAA;AAGT,EAAA;AAIX;AAKgC;AAC9B,EAAA;AAGC;AACmC,EAAA;AACZ,IAAA;AACb,MAAA;AACT,IAAA;AAEsB,IAAA;AACb,MAAA;AACT,IAAA;AAEO,IAAA;AACT,EAAA;AAEiC,EAAA;AAG1B,EAAA;AACa,oBAAA;AACQ,MAAA;AAAE,MAAA;AAC1B,IAAA;AACe,oBAAA;AACZ,MAAA;AAAA,MAAA;AACmC,QAAA;AACD,QAAA;AAAA,MAAA;AAErC,IAAA;AACF,EAAA;AAEJ;AAKoE;AACxD,EAAA;AACC,EAAA;AACJ,EAAA;AACT;AAK4E;AAChE,EAAA;AACC,EAAA;AACJ,EAAA;AACT;AAQM;AACJ,EAAA;AACO,IAAA;AACE,IAAA;AACG,IAAA;AACC,IAAA;AACb,EAAA;AACA,EAAA;AACO,IAAA;AACE,IAAA;AACG,IAAA;AACC,IAAA;AACb,EAAA;AACA,EAAA;AACK,IAAA;AACE,IAAA;AACE,IAAA;AACA,IAAA;AACG,IAAA;AACZ,EAAA;AACA,EAAA;AACK,IAAA;AACE,IAAA;AACE,IAAA;AACA,IAAA;AACG,IAAA;AACZ,EAAA;AACA,EAAA;AACK,IAAA;AACE,IAAA;AACE,IAAA;AACA,IAAA;AACT,EAAA;AACA,EAAA;AACK,IAAA;AACE,IAAA;AACE,IAAA;AACA,IAAA;AACT,EAAA;AACF;AAKmE;AACjE,EAAA;AACO,IAAA;AACE,IAAA;AACG,IAAA;AACC,IAAA;AACb,EAAA;AACA,EAAA;AACO,IAAA;AACE,IAAA;AACG,IAAA;AACC,IAAA;AACyD,IAAA;AACtE,EAAA;AACA,EAAA;AACK,IAAA;AACE,IAAA;AACE,IAAA;AACA,IAAA;AACT,EAAA;AACA,EAAA;AACK,IAAA;AACE,IAAA;AACE,IAAA;AACA,IAAA;AACT,EAAA;AACA,EAAA;AACK,IAAA;AACE,IAAA;AACE,IAAA;AACA,IAAA;AACT,EAAA;AACA,EAAA;AACK,IAAA;AACE,IAAA;AACE,IAAA;AACA,IAAA;AAELF,IAAAA;AAEJ,EAAA;AACF;AAMwC;AACtC,EAAA;AACA,EAAA;AACU,EAAA;AACsD;AACxB,EAAA;AAGhBG,EAAAA;AACY,IAAA;AACtB,IAAA;AACd,EAAA;AAGsD,EAAA;AACf,IAAA;AACA,IAAA;AACC,IAAA;AACC,IAAA;AACA,IAAA;AACF,IAAA;AAC9B,IAAA;AACQ,EAAA;AAE0B,EAAA;AACD,IAAA;AACV,IAAA;AAC9B,IAAA;AACe,EAAA;AAGe,EAAA;AAGD,EAAA;AAGe,EAAA;AAC5C,IAAA;AACF,EAAA;AAC4C,EAAA;AAAI,IAAA;AAEhD,EAAA;AAG2B,EAAA;AACmBN,EAAAA;AAKnB,EAAA;AACmBA,EAAAA;AAKnB,EAAA;AAG8B,EAAA;AAC3C,IAAA;AACI,IAAA;AACL,IAAA;AACG,IAAA;AACf,EAAA;AAGkCO,EAAAA;AACjC,IAAA;AACF,EAAA;AACmD,EAAA;AACA,EAAA;AACE,EAAA;AACL,EAAA;AACQ,EAAA;AACtB,EAAA;AAGlB,EAAA;AACuB,IAAA;AACX,EAAA;AAEZ,EAAA;AACiB,IAAA;AACX,EAAA;AAEN,EAAA;AACO,IAAA;AACX,EAAA;AAEI,EAAA;AACgB,IAAA;AACX,EAAA;AAEL,EAAA;AACS,IAAA;AACX,EAAA;AAEE,EAAA;AACM,IAAA;AACX,EAAA;AAEK,EAAA;AACe,IAAA;AACX,EAAA;AAGd,EAAA;AACE,IAAA;AACG,IAAA;AACF,IAAA;AACQ,IAAA;AACS,EAAA;AAGpB,EAAA;AACE,IAAA;AACG,IAAA;AACK,IAAA;AACP,IAAA;AACQ,IAAA;AACY,EAAA;AAGvB,EAAA;AACE,IAAA;AACG,IAAA;AACK,IAAA;AACP,IAAA;AACQ,IAAA;AACW,EAAA;AAEtB,EAAA;AACE,IAAA;AACG,IAAA;AACK,IAAA;AACP,IAAA;AACP,IAAA;AAC0B,EAAA;AAGJ,EAAA;AAEiB,EAAA;AAC3B,IAAA;AACsB,IAAA;AACI,IAAA;AACL,IAAA;AACG,IAAA;AAC7B,IAAA;AACG,EAAA;AAGI,EAAA;AACmB,IAAA;AAC/B,MAAA;AACF,IAAA;AACuC,IAAA;AACrB,MAAA;AAClB,IAAA;AACwC,IAAA;AACJ,IAAA;AACb,EAAA;AAGID,EAAAA;AAEM,IAAA;AACF,MAAA;AAClB,QAAA;AACF,UAAA;AAEW,UAAA;AAChB,QAAA;AACF,MAAA;AAC+B,MAAA;AACtB,QAAA;AACF,UAAA;AAED,UAAA;AAEJ,QAAA;AACF,MAAA;AACO,MAAA;AACR,IAAA;AACO,IAAA;AACZ,EAAA;AAGyBV,EAAAA;AAId,IAAA;AAK2B,MAAA;AACM,MAAA;AACf,MAAA;AACW,MAAA;AAGtB,MAAA;AAEX,MAAA;AAIG,MAAA;AACJ,QAAA;AACuC,UAAA;AAC7B,UAAA;AACG,UAAA;AACM,UAAA;AACS,UAAA;AACI,UAAA;AACL,UAAA;AACG,UAAA;AAC5B,UAAA;AACO,UAAA;AACE,UAAA;AACF,UAAA;AACI,UAAA;AACb,QAAA;AACA,QAAA;AACF,MAAA;AACF,IAAA;AACuB,IAAA;AACzB,EAAA;AAEuBA,EAAAA;AAInB,IAAA;AAIuC,MAAA;AACrC,QAAA;AACF,MAAA;AAEkC,MAAA;AACM,MAAA;AACf,MAAA;AAGV,MAAA;AAEb,MAAA;AAIkB,MAAA;AACF,QAAA;AACR,QAAA;AACG,QAAA;AACM,QAAA;AACS,QAAA;AACI,QAAA;AACL,QAAA;AACG,QAAA;AACtB,QAAA;AACC,QAAA;AACE,QAAA;AACF,QAAA;AACI,QAAA;AACZ,MAAA;AACH,IAAA;AACc,IAAA;AAChB,EAAA;AAEgCA,EAAAA;AAG5B,IAAA;AAEkC,MAAA;AACT,MAAA;AAEE,MAAA;AACW,QAAA;AACV,QAAA;AACI,QAAA;AACL,QAAA;AACG,QAAA;AACjB,QAAA;AACZ,MAAA;AACH,IAAA;AACqB,IAAA;AACvB,EAAA;AAKmCA,EAAAA;AAI/B,IAAA;AAIkC,MAAA;AACM,MAAA;AACf,MAAA;AACM,MAAA;AACK,MAAA;AAGtB,MAAA;AAEX,MAAA;AAIG,MAAA;AACJ,QAAA;AACmB,UAAA;AACoB,UAAA;AAC7B,UAAA;AACG,UAAA;AACM,UAAA;AACS,UAAA;AACI,UAAA;AACL,UAAA;AACG,UAAA;AACjB,UAAA;AACb,QAAA;AACA,QAAA;AACF,MAAA;AACF,IAAA;AACwB,IAAA;AAC1B,EAAA;AAG2BA,EAAAA;AACN,IAAA;AACC,MAAA;AACmB,MAAA;AACnC,QAAA;AACF,MAAA;AACwC,MAAA;AAEd,MAAA;AACc,QAAA;AACT,QAAA;AACF,UAAA;AACpB,QAAA;AAC4B,UAAA;AACnC,QAAA;AAC2B,QAAA;AACG,UAAA;AACvB,QAAA;AACsB,UAAA;AAC7B,QAAA;AACO,QAAA;AACR,MAAA;AAGD,MAAA;AAC6B,QAAA;AACN,QAAA;AACrB,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AAGwB,MAAA;AACtB,QAAA;AAC6B,UAAA;AACN,UAAA;AACrB,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACF,QAAA;AACK,MAAA;AACL,QAAA;AAC6B,UAAA;AACN,UAAA;AACrB,UAAA;AACA,UAAA;AACF,QAAA;AACA,QAAA;AAC6B,UAAA;AACN,UAAA;AACrB,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACF,QAAA;AACF,MAAA;AACmC,MAAA;AACrC,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAE8BA,EAAAA;AACT,IAAA;AACI,MAAA;AACa,MAAA;AAChC,QAAA;AACF,MAAA;AACkC,MAAA;AACR,MAAA;AACc,QAAA;AACH,QAAA;AACR,UAAA;AACpB,QAAA;AACmB,UAAA;AAC1B,QAAA;AACmC,QAAA;AACL,UAAA;AACvB,QAAA;AACsB,UAAA;AAC7B,QAAA;AACO,QAAA;AACR,MAAA;AACH,IAAA;AACgB,IAAA;AAClB,EAAA;AAEkCA,EAAAA;AACL,IAAA;AACiB,MAAA;AACxC,QAAA;AACF,MAAA;AAGwC,MAAA;AACA,MAAA;AAEP,MAAA;AACG,MAAA;AACX,MAAA;AACzB,MAAA;AAEuC,MAAA;AACV,MAAA;AAC/B,IAAA;AACmB,IAAA;AACrB,EAAA;AAE4BA,EAAAA;AACI,IAAA;AAEZ,MAAA;AACW,MAAA;AACzB,QAAA;AACF,MAAA;AAEuC,MAAA;AAGvC,MAAA;AAC6B,QAAA;AAC3B,QAAA;AACF,MAAA;AAEwB,MAAA;AACtB,QAAA;AAC6B,UAAA;AAC3B,UAAA;AACF,QAAA;AACK,MAAA;AACU,QAAA;AACf,QAAA;AAC6B,UAAA;AAC3B,UAAA;AACF,QAAA;AACF,MAAA;AACF,IAAA;AACmC,IAAA;AACrC,EAAA;AAE4BA,EAAAA;AACP,IAAA;AACF,MAAA;AAEoB,MAAA;AACjC,QAAA;AACF,MAAA;AAE0B,MAAA;AAEgB,MAAA;AACxC,QAAA;AAC6B,UAAA;AACN,UAAA;AACvB,QAAA;AACK,MAAA;AACL,QAAA;AAC6B,UAAA;AACN,UAAA;AACvB,QAAA;AACF,MAAA;AACF,IAAA;AACiC,IAAA;AACnC,EAAA;AAE2BA,EAAAA;AACS,IAAA;AAEX,MAAA;AACA,MAAA;AAGrB,MAAA;AAC6B,QAAA;AACN,QAAA;AACvB,MAAA;AAEwB,MAAA;AACtB,QAAA;AAC6B,UAAA;AACN,UAAA;AACvB,QAAA;AACK,MAAA;AACL,QAAA;AAC6B,UAAA;AACN,UAAA;AACvB,QAAA;AACA,QAAA;AAC6B,UAAA;AACN,UAAA;AACvB,QAAA;AACF,MAAA;AACwB,MAAA;AAC1B,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEmCA,EAAAA;AACI,IAAA;AACZ,MAAA;AACD,MAAA;AACG,MAAA;AAEiB,MAAA;AAClC,QAAA;AACE,QAAA;AACI,QAAA;AACL,QAAA;AACE,QAAA;AACI,QAAA;AACf,MAAA;AAE0C,MAAA;AACL,MAAA;AACN,MAAA;AACN,MAAA;AACT,MAAA;AAGF,MAAA;AAIX,MAAA;AAIH,MAAA;AACE,QAAA;AACuC,UAAA;AACnB,UAAA;AACP,UAAA;AACM,UAAA;AACa,UAAA;AACI,UAAA;AACL,UAAA;AACG,UAAA;AACf,UAAA;AACV,UAAA;AACP,UAAA;AACA,UAAA;AACwB,UAAA;AAC1B,QAAA;AACA,QAAA;AACF,MAAA;AACF,IAAA;AACuB,IAAA;AACzB,EAAA;AAEmCA,EAAAA;AACI,IAAA;AACJ,MAAA;AAC7B,QAAA;AACF,MAAA;AAEuB,MAAA;AACC,MAAA;AACF,MAAA;AACG,MAAA;AAEiB,MAAA;AAC3B,QAAA;AACQ,QAAA;AACL,QAAA;AACD,QAAA;AACF,QAAA;AACf,MAAA;AAGqB,MAAA;AAEgB,MAAA;AAEZ,MAAA;AACM,MAAA;AAE7B,MAAA;AAIA,MAAA;AAIY,MAAA;AACI,QAAA;AACE,QAAA;AACP,QAAA;AACM,QAAA;AACa,QAAA;AACI,QAAA;AACL,QAAA;AACG,QAAA;AACf,QAAA;AACE,QAAA;AACnB,QAAA;AACA,QAAA;AACwB,QAAA;AACzB,MAAA;AACH,IAAA;AACc,IAAA;AAChB,EAAA;AAE2C,EAAA;AACtB,IAAA;AACM,MAAA;AACb,MAAA;AACX,IAAA;AACyB,IAAA;AACvB,EAAA;AAEsC,EAAA;AACtB,IAAA;AACF,MAAA;AACE,MAAA;AAClB,IAAA;AACyB,IAAA;AACvB,EAAA;AAG8B,EAAA;AACS,EAAA;AAE5B,EAAA;AAEA,IAAA;AACc,IAAA;AACH,IAAA;AAGY,IAAA;AAEV,IAAA;AACH,IAAA;AAGK,IAAA;AACO,MAAA;AACnC,IAAA;AACA,EAAA;AACD,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACD,EAAA;AAGe,EAAA;AAEA,IAAA;AACwB,IAAA;AAGtC,IAAA;AAC6B,MAAA;AACN,MAAA;AACvB,IAAA;AAEwB,IAAA;AACtB,MAAA;AAC6B,QAAA;AACN,QAAA;AACvB,MAAA;AACK,IAAA;AACL,MAAA;AAC6B,QAAA;AACN,QAAA;AACvB,MAAA;AACA,MAAA;AAC6B,QAAA;AACN,QAAA;AACvB,MAAA;AACF,IAAA;AACwB,IAAA;AACvB,EAAA;AACD,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACD,EAAA;AAGyC,EAAA;AAC1B,EAAA;AAEA,IAAA;AAE6B,IAAA;AACN,IAAA;AAGC,IAAA;AAGtC,IAAA;AAC6B,MAAA;AACN,MAAA;AACvB,IAAA;AAC4B,EAAA;AAEvB,EAAA;AAAA;AAEL,IAAA;AACA,IAAA;AAAA;AAGA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AAAA;AAGA,IAAA;AACA,IAAA;AAAA;AAGA,IAAA;AAAA;AAGiB,IAAA;AACT,MAAA;AACG,MAAA;AACF,MAAA;AACT,IAAA;AACoB,IAAA;AACZ,MAAA;AACG,MAAA;AACK,MAAA;AACP,MAAA;AACT,IAAA;AACkB,IAAA;AACV,MAAA;AACG,MAAA;AACK,MAAA;AACP,MAAA;AACT,IAAA;AACqB,IAAA;AACb,MAAA;AACG,MAAA;AACK,MAAA;AACP,MAAA;AACT,IAAA;AAAA;AAGA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AAAA;AAGc,IAAA;AACJ,MAAA;AACC,MAAA;AACI,MAAA;AACgB,QAAA;AACJ,QAAA;AACzB,MAAA;AACF,IAAA;AACc,IAAA;AACJ,MAAA;AACC,MAAA;AACI,MAAA;AACgB,QAAA;AACJ,QAAA;AACzB,MAAA;AACF,IAAA;AACc,IAAA;AACJ,MAAA;AAC8B,MAAA;AACE,MAAA;AAC1C,IAAA;AAAA;AAGA,IAAA;AACA,IAAA;AACF,EAAA;AACF;ADg4BgD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-Y4IGIWP4.js","sourcesContent":[null,"import { useEffect, useCallback, useState, useMemo, useRef } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport Badge from '../Badge/Badge';\nimport { useGeneralOverview } from '../GeneralOverviewSection';\nimport {\n useSimulatedOverview,\n useAggregatedOverview,\n} from '../SimulatedStudentsOverview';\nimport { useSimulatedContents } from '../SimulatedContentsPerformance';\nimport { ESSAY_AREA_ID } from '../AreaKnowledgeSelector';\nimport { SIMULATED_PERFORMANCE_TAG_CONFIG } from '../SimulatedStudentDetailsModal';\nimport { formatScore } from '../../utils/utils';\nimport { getAggregationTypeByProfile } from '../../utils/profileAggregation';\nimport type { ColumnConfig } from '../TableProvider';\nimport type { StudentsHighlightPeriod } from '../../hooks/useStudentsHighlight';\nimport { ScoreType } from '../../types/common';\nimport type { SimulatedFilters } from '../SimulatedFilters/types';\nimport type { SimulatedStudentItem } from '../SimulatedStudentsOverview/types';\nimport type { SimulatedContentItem } from '../SimulatedContentsPerformance/types';\nimport {\n SimulatedViewTab,\n type UseSimulatedPerformanceOptions,\n type UseSimulatedPerformanceReturn,\n} from './types';\n\n/**\n * Performance tag cell renderer for simulated exams\n */\nfunction SimulatedPerformanceTagCell({\n row,\n}: {\n readonly row: SimulatedStudentItem;\n}) {\n const tagConfig = SIMULATED_PERFORMANCE_TAG_CONFIG[row.performance];\n\n return (\n <Badge variant=\"solid\" action={tagConfig.variant} size=\"small\">\n {tagConfig.label}\n </Badge>\n );\n}\n\n/**\n * Performance cell renderer for contents/skills with progress bar\n */\nfunction ContentPerformanceCell({\n percentage,\n}: {\n readonly percentage: number;\n}) {\n const getColorClass = (): string => {\n if (percentage >= 70) {\n return 'bg-success-500';\n }\n\n if (percentage >= 50) {\n return 'bg-warning-500';\n }\n\n return 'bg-error-500';\n };\n\n const colorClass = getColorClass();\n\n return (\n <div className=\"flex items-center justify-center gap-2\">\n <span className=\"text-sm text-text-950 min-w-[40px]\">\n {Math.round(percentage)}%\n </span>\n <div className=\"w-16 h-2 bg-background-100 rounded-full overflow-hidden\">\n <div\n className={`h-full rounded-full ${colorClass}`}\n style={{ width: `${percentage}%` }}\n />\n </div>\n </div>\n );\n}\n\n/**\n * Shared defaults for centered columns\n */\nconst centeredColumn: Partial<ColumnConfig<SimulatedStudentItem>> = {\n sortable: true,\n className: 'py-3 px-4 text-center',\n align: 'center',\n};\n\n/**\n * Shared defaults for centered columns (contents table)\n */\nconst centeredContentsColumn: Partial<ColumnConfig<SimulatedContentItem>> = {\n sortable: true,\n className: 'py-3 px-4 text-center',\n align: 'center',\n};\n\n/**\n * Base columns for simulated exams view (without render functions that depend on scoreType)\n */\nconst simulatedTableColumnsBase: Omit<\n ColumnConfig<SimulatedStudentItem>,\n 'render'\n>[] = [\n {\n key: 'name',\n label: 'Nome',\n sortable: true,\n className: 'py-3 px-4 text-start',\n },\n {\n key: 'school',\n label: 'Escola',\n sortable: false,\n className: 'py-3 px-4 text-start',\n },\n {\n ...centeredColumn,\n key: 'schoolYear',\n label: 'Ano',\n width: '80px',\n sortable: false,\n },\n {\n ...centeredColumn,\n key: 'class',\n label: 'Turma',\n width: '80px',\n sortable: false,\n },\n {\n ...centeredColumn,\n key: 'average',\n label: 'Média',\n width: '100px',\n },\n {\n ...centeredColumn,\n key: 'performance',\n label: 'Desempenho',\n width: '160px',\n },\n];\n\n/**\n * Table columns for simulated contents/skills view\n */\nconst contentsTableColumns: ColumnConfig<SimulatedContentItem>[] = [\n {\n key: 'contentName',\n label: 'Habilidade',\n sortable: true,\n className: 'py-3 px-4 text-start',\n },\n {\n key: 'subject',\n label: 'Componente',\n sortable: false,\n className: 'py-3 px-4 text-start',\n render: (_value: unknown, row: SimulatedContentItem) => row.subject.name,\n },\n {\n ...centeredContentsColumn,\n key: 'simulatedExamsCount',\n label: 'Simulados',\n width: '100px',\n },\n {\n ...centeredContentsColumn,\n key: 'questionsCount',\n label: 'Questões',\n width: '100px',\n },\n {\n ...centeredContentsColumn,\n key: 'studentsCount',\n label: 'Estudantes',\n width: '100px',\n },\n {\n ...centeredContentsColumn,\n key: 'performance',\n label: 'Desempenho',\n width: '160px',\n render: (_value: unknown, row: SimulatedContentItem) => (\n <ContentPerformanceCell percentage={row.performance.correctPercentage} />\n ),\n },\n];\n\n/**\n * Hook de orquestração para a funcionalidade de Simulados.\n * Gerencia estados, query params, carregamento de dados e handlers.\n */\nexport function useSimulatedPerformance({\n api,\n profileName,\n enabled = true,\n}: UseSimulatedPerformanceOptions): UseSimulatedPerformanceReturn {\n const [searchParams, setSearchParams] = useSearchParams();\n\n // === Aggregation Type based on Profile ===\n const aggregationType = useMemo(\n () => getAggregationTypeByProfile(profileName),\n [profileName]\n );\n\n // === Query Params ===\n const period = useMemo((): StudentsHighlightPeriod => {\n const periodParam = searchParams.get('period');\n if (periodParam === '7_DAYS') return '7_DAYS';\n if (periodParam === '1_MONTH') return '1_MONTH';\n if (periodParam === '3_MONTHS') return '3_MONTHS';\n if (periodParam === '6_MONTHS') return '6_MONTHS';\n if (periodParam === '1_YEAR') return '1_YEAR';\n return '1_MONTH';\n }, [searchParams]);\n\n const scoreType = useMemo((): ScoreType => {\n const scoreTypeParam = searchParams.get('scoreType');\n if (scoreTypeParam === 'tri') return ScoreType.TRI;\n return ScoreType.PERCENTAGE;\n }, [searchParams]);\n\n // === Estados de Seleção ===\n const [selectedAreaKnowledgeId, setSelectedAreaKnowledgeId] = useState<\n string | null\n >(null);\n const [selectedAreaRelatedIds, setSelectedAreaRelatedIds] = useState<\n string[]\n >([]);\n const [selectedSubjectId, setSelectedSubjectId] = useState<string | null>(\n null\n );\n const [simulatedViewTab, setSimulatedViewTab] = useState<SimulatedViewTab>(\n SimulatedViewTab.STUDENTS\n );\n\n // === Estados de Modais ===\n const [isStudentModalOpen, setIsStudentModalOpen] = useState(false);\n const [selectedStudent, setSelectedStudent] = useState<{\n userInstitutionId: string;\n name: string;\n } | null>(null);\n\n const [isContentModalOpen, setIsContentModalOpen] = useState(false);\n const [selectedContent, setSelectedContent] = useState<{\n contentId: string;\n contentName: string;\n } | null>(null);\n\n const [isFiltersModalOpen, setIsFiltersModalOpen] = useState(false);\n\n // === Filtros ===\n const [filters, setFilters] = useState<SimulatedFilters>({\n schoolIds: [],\n schoolYearIds: [],\n classIds: [],\n studentsIds: [],\n });\n\n // === Refs para evitar closure issues ===\n const selectedAreaKnowledgeIdRef = useRef<string | null>(\n selectedAreaKnowledgeId\n );\n const selectedSubjectIdRef = useRef<string | null>(selectedSubjectId);\n const filtersRef = useRef<SimulatedFilters>(filters);\n const simulatedViewTabRef = useRef<SimulatedViewTab>(simulatedViewTab);\n const scoreTypeRef = useRef<ScoreType>(scoreType);\n const periodRef = useRef<StudentsHighlightPeriod>(period);\n const aggregationTypeRef = useRef(aggregationType);\n\n // Keep refs in sync\n useEffect(() => {\n selectedAreaKnowledgeIdRef.current = selectedAreaKnowledgeId;\n }, [selectedAreaKnowledgeId]);\n\n useEffect(() => {\n selectedSubjectIdRef.current = selectedSubjectId;\n }, [selectedSubjectId]);\n\n useEffect(() => {\n filtersRef.current = filters;\n }, [filters]);\n\n useEffect(() => {\n simulatedViewTabRef.current = simulatedViewTab;\n }, [simulatedViewTab]);\n\n useEffect(() => {\n scoreTypeRef.current = scoreType;\n }, [scoreType]);\n\n useEffect(() => {\n periodRef.current = period;\n }, [period]);\n\n useEffect(() => {\n aggregationTypeRef.current = aggregationType;\n }, [aggregationType]);\n\n // === Hooks de API ===\n const {\n data: generalOverviewData,\n loading: generalOverviewLoading,\n error: generalOverviewError,\n fetchOverview: fetchGeneralOverview,\n } = useGeneralOverview(api);\n\n // Aggregated overview based on profile (classes/municipalities/students)\n const {\n data: aggregatedData,\n loading: aggregatedLoading,\n isRefreshing: aggregatedRefreshing,\n error: aggregatedError,\n fetchOverview: fetchAggregatedOverview,\n } = useAggregatedOverview(api);\n\n // Paginated students overview for table\n const {\n data: simulatedData,\n loading: simulatedLoading,\n isRefreshing: simulatedRefreshing,\n error: simulatedError,\n fetchOverview: fetchSimulatedOverview,\n } = useSimulatedOverview(api);\n\n const {\n data: contentsData,\n loading: contentsLoading,\n isRefreshing: contentsRefreshing,\n error: contentsError,\n fetchContents,\n } = useSimulatedContents(api);\n\n // === Valores derivados ===\n const isEssaySelected = selectedAreaKnowledgeId === ESSAY_AREA_ID;\n\n const activeFiltersCount = useMemo(() => {\n let count = 0;\n if (filters.schoolIds.length > 0) count++;\n if (filters.schoolYearIds.length > 0) count++;\n if (filters.classIds.length > 0) count++;\n if (filters.studentsIds.length > 0) count++;\n return count;\n }, [filters]);\n\n // Sync selectedAreaRelatedIds when generalOverviewData is refreshed\n useEffect(() => {\n if (!selectedAreaKnowledgeId || !generalOverviewData?.areas) {\n return;\n }\n const area = generalOverviewData.areas.find(\n (a) => a.id === selectedAreaKnowledgeId\n );\n const relatedIds = area?.relatedIds ?? [selectedAreaKnowledgeId];\n setSelectedAreaRelatedIds(relatedIds);\n }, [generalOverviewData, selectedAreaKnowledgeId]);\n\n // === Colunas das Tabelas (memoizadas) ===\n const studentsTableColumns = useMemo(\n (): ColumnConfig<SimulatedStudentItem>[] =>\n simulatedTableColumnsBase.map((col) => {\n if (col.key === 'average') {\n return {\n ...col,\n render: (_value: unknown, row: SimulatedStudentItem) =>\n formatScore(row.average, scoreType),\n };\n }\n if (col.key === 'performance') {\n return {\n ...col,\n render: (_value: unknown, row: SimulatedStudentItem) => (\n <SimulatedPerformanceTagCell row={row} />\n ),\n };\n }\n return col as ColumnConfig<SimulatedStudentItem>;\n }),\n [scoreType]\n );\n\n // === Funções de Carregamento ===\n const loadStudentsData = useCallback(\n async (\n areaKnowledgeId: string | null,\n subjectId: string | null,\n page = 1,\n refresh = false,\n periodOverride?: StudentsHighlightPeriod,\n scoreTypeOverride?: ScoreType\n ) => {\n const currentFilters = filtersRef.current;\n const currentPeriod = periodOverride ?? periodRef.current;\n const currentScoreType = scoreTypeOverride ?? scoreTypeRef.current;\n const isEssay = areaKnowledgeId === ESSAY_AREA_ID;\n\n const effectiveSubjectId =\n !isEssay && subjectId && subjectId !== 'all' ? subjectId : undefined;\n const effectiveAreaKnowledgeId =\n !isEssay && areaKnowledgeId && areaKnowledgeId !== 'all'\n ? areaKnowledgeId\n : undefined;\n\n await fetchSimulatedOverview(\n {\n simulationType: isEssay ? 'essays' : 'enem-1',\n period: currentPeriod,\n subjectId: effectiveSubjectId,\n areaKnowledgeId: effectiveAreaKnowledgeId,\n schoolIds: currentFilters.schoolIds,\n schoolYearIds: currentFilters.schoolYearIds,\n classIds: currentFilters.classIds,\n studentsIds: currentFilters.studentsIds,\n page,\n limit: 10,\n orderBy: 'name',\n order: 'asc',\n scoreType: currentScoreType,\n },\n refresh\n );\n },\n [fetchSimulatedOverview]\n );\n\n const loadSkillsData = useCallback(\n async (\n areaKnowledgeId: string | null,\n subjectId: string | null,\n periodOverride?: StudentsHighlightPeriod,\n scoreTypeOverride?: ScoreType\n ) => {\n // Essays não têm conteúdos/habilidades\n if (areaKnowledgeId === ESSAY_AREA_ID) {\n return;\n }\n\n const currentFilters = filtersRef.current;\n const currentPeriod = periodOverride ?? periodRef.current;\n const currentScoreType = scoreTypeOverride ?? scoreTypeRef.current;\n\n const effectiveSubjectId =\n subjectId && subjectId !== 'all' ? subjectId : undefined;\n const effectiveAreaKnowledgeId =\n areaKnowledgeId && areaKnowledgeId !== 'all'\n ? areaKnowledgeId\n : undefined;\n\n await fetchContents({\n simulationType: 'enem-1',\n period: currentPeriod,\n subjectId: effectiveSubjectId,\n areaKnowledgeId: effectiveAreaKnowledgeId,\n schoolIds: currentFilters.schoolIds,\n schoolYearIds: currentFilters.schoolYearIds,\n classIds: currentFilters.classIds,\n studentsIds: currentFilters.studentsIds,\n page: 1,\n limit: 10,\n orderBy: 'correctPercentage',\n order: 'asc',\n scoreType: currentScoreType,\n });\n },\n [fetchContents]\n );\n\n const loadGeneralOverviewData = useCallback(\n async (\n periodOverride?: StudentsHighlightPeriod,\n scoreTypeOverride?: ScoreType\n ) => {\n const currentFilters = filtersRef.current;\n const currentScoreType = scoreTypeOverride ?? scoreTypeRef.current;\n\n await fetchGeneralOverview({\n period: periodOverride ?? periodRef.current,\n schoolIds: currentFilters.schoolIds,\n schoolYearIds: currentFilters.schoolYearIds,\n classIds: currentFilters.classIds,\n studentsIds: currentFilters.studentsIds,\n scoreType: currentScoreType,\n });\n },\n [fetchGeneralOverview]\n );\n\n /**\n * Load aggregated overview data based on profile (classes/municipalities/students)\n */\n const loadAggregatedOverviewData = useCallback(\n async (\n areaKnowledgeId: string | null,\n subjectId: string | null,\n refresh = false,\n periodOverride?: StudentsHighlightPeriod,\n scoreTypeOverride?: ScoreType\n ) => {\n const currentFilters = filtersRef.current;\n const currentPeriod = periodOverride ?? periodRef.current;\n const currentScoreType = scoreTypeOverride ?? scoreTypeRef.current;\n const currentAggregationType = aggregationTypeRef.current;\n const isEssay = areaKnowledgeId === ESSAY_AREA_ID;\n\n const effectiveSubjectId =\n !isEssay && subjectId && subjectId !== 'all' ? subjectId : undefined;\n const effectiveAreaKnowledgeId =\n !isEssay && areaKnowledgeId && areaKnowledgeId !== 'all'\n ? areaKnowledgeId\n : undefined;\n\n await fetchAggregatedOverview(\n {\n aggregationType: currentAggregationType,\n simulationType: isEssay ? 'essays' : 'enem-1',\n period: currentPeriod,\n subjectId: effectiveSubjectId,\n areaKnowledgeId: effectiveAreaKnowledgeId,\n schoolIds: currentFilters.schoolIds,\n schoolYearIds: currentFilters.schoolYearIds,\n classIds: currentFilters.classIds,\n studentsIds: currentFilters.studentsIds,\n scoreType: currentScoreType,\n },\n refresh\n );\n },\n [fetchAggregatedOverview]\n );\n\n // === Handlers ===\n const handlePeriodChange = useCallback(\n (value: string) => {\n const newPeriod = value as StudentsHighlightPeriod;\n if (newPeriod === periodRef.current) {\n return;\n }\n const effectiveScoreType = scoreTypeRef.current;\n\n setSearchParams((prev) => {\n const newParams = new URLSearchParams(prev);\n if (newPeriod === '1_MONTH') {\n newParams.delete('period');\n } else {\n newParams.set('period', newPeriod);\n }\n if (effectiveScoreType === 'percentage') {\n newParams.delete('scoreType');\n } else {\n newParams.set('scoreType', effectiveScoreType);\n }\n return newParams;\n });\n\n // Reload aggregated overview\n loadAggregatedOverviewData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current,\n false,\n newPeriod,\n effectiveScoreType\n );\n\n // Reload data with new period\n if (simulatedViewTabRef.current === SimulatedViewTab.STUDENTS) {\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current,\n 1,\n false,\n newPeriod,\n effectiveScoreType\n );\n } else {\n loadSkillsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current,\n newPeriod,\n effectiveScoreType\n );\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current,\n 1,\n false,\n newPeriod,\n effectiveScoreType\n );\n }\n loadGeneralOverviewData(newPeriod, effectiveScoreType);\n },\n [\n setSearchParams,\n loadStudentsData,\n loadSkillsData,\n loadGeneralOverviewData,\n loadAggregatedOverviewData,\n ]\n );\n\n const handleScoreTypeChange = useCallback(\n (value: string) => {\n const newScoreType = value as ScoreType;\n if (newScoreType === scoreTypeRef.current) {\n return;\n }\n const effectivePeriod = periodRef.current;\n setSearchParams((prev) => {\n const newParams = new URLSearchParams(prev);\n if (effectivePeriod === '1_MONTH') {\n newParams.delete('period');\n } else {\n newParams.set('period', effectivePeriod);\n }\n if (newScoreType === 'percentage') {\n newParams.delete('scoreType');\n } else {\n newParams.set('scoreType', newScoreType);\n }\n return newParams;\n });\n },\n [setSearchParams]\n );\n\n const handleAreaKnowledgeChange = useCallback(\n (areaId: string | null) => {\n if (areaId === selectedAreaKnowledgeIdRef.current) {\n return;\n }\n\n // Find the area to get relatedIds (for merged areas with same name)\n const area = generalOverviewData?.areas.find((a) => a.id === areaId);\n const relatedIds = area?.relatedIds ?? (areaId ? [areaId] : []);\n\n setSelectedAreaKnowledgeId(areaId);\n setSelectedAreaRelatedIds(relatedIds);\n setSelectedSubjectId(null);\n setSimulatedViewTab(SimulatedViewTab.STUDENTS);\n\n loadAggregatedOverviewData(areaId, null);\n loadStudentsData(areaId, null);\n },\n [loadStudentsData, loadAggregatedOverviewData, generalOverviewData]\n );\n\n const handleSubjectChange = useCallback(\n (subjectId: string | null) => {\n const effectiveSubjectId =\n subjectId === 'all' || subjectId === null ? null : subjectId;\n if (effectiveSubjectId === selectedSubjectIdRef.current) {\n return;\n }\n\n setSelectedSubjectId(effectiveSubjectId);\n\n // Always reload aggregated overview\n loadAggregatedOverviewData(\n selectedAreaKnowledgeIdRef.current,\n effectiveSubjectId\n );\n\n if (simulatedViewTabRef.current === SimulatedViewTab.STUDENTS) {\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n effectiveSubjectId\n );\n } else {\n loadSkillsData(selectedAreaKnowledgeIdRef.current, effectiveSubjectId);\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n effectiveSubjectId\n );\n }\n },\n [loadStudentsData, loadSkillsData, loadAggregatedOverviewData]\n );\n\n const handleViewTabChange = useCallback(\n (value: string) => {\n const newTab = value as SimulatedViewTab;\n\n if (newTab === simulatedViewTabRef.current) {\n return;\n }\n\n setSimulatedViewTab(newTab);\n\n if (newTab === SimulatedViewTab.STUDENTS) {\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n } else {\n loadSkillsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n }\n },\n [loadStudentsData, loadSkillsData]\n );\n\n const handleFiltersApply = useCallback(\n (newFilters: SimulatedFilters) => {\n // Atualizar ref ANTES para que as funções de load usem os novos valores\n filtersRef.current = newFilters;\n setFilters(newFilters);\n\n // Reload aggregated overview\n loadAggregatedOverviewData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n\n if (simulatedViewTabRef.current === SimulatedViewTab.STUDENTS) {\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n } else {\n loadSkillsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n }\n loadGeneralOverviewData();\n },\n [\n loadStudentsData,\n loadSkillsData,\n loadGeneralOverviewData,\n loadAggregatedOverviewData,\n ]\n );\n\n const handleStudentsParamsChange = useCallback(\n (params: Record<string, unknown>) => {\n const newPage = params.page as number;\n const sortBy = params.sortBy as string;\n const sortOrder = params.sortOrder as string;\n\n const sortByMap: Record<string, string> = {\n name: 'name',\n school: 'school',\n schoolYear: 'schoolYear',\n class: 'class',\n average: 'average',\n performance: 'performance',\n };\n\n const orderBy = sortBy ? (sortByMap[sortBy] ?? 'name') : 'name';\n const order = sortOrder === 'desc' ? 'desc' : 'asc';\n const currentAreaKnowledgeId = selectedAreaKnowledgeIdRef.current;\n const currentSubjectId = selectedSubjectIdRef.current;\n const isEssay = currentAreaKnowledgeId === ESSAY_AREA_ID;\n\n const effectiveSubjectId =\n !isEssay && currentSubjectId && currentSubjectId !== 'all'\n ? currentSubjectId\n : undefined;\n const effectiveAreaKnowledgeId =\n !isEssay && currentAreaKnowledgeId && currentAreaKnowledgeId !== 'all'\n ? currentAreaKnowledgeId\n : undefined;\n\n fetchSimulatedOverview(\n {\n simulationType: isEssay ? 'essays' : 'enem-1',\n period: periodRef.current,\n subjectId: effectiveSubjectId,\n areaKnowledgeId: effectiveAreaKnowledgeId,\n schoolIds: filtersRef.current.schoolIds,\n schoolYearIds: filtersRef.current.schoolYearIds,\n classIds: filtersRef.current.classIds,\n studentsIds: filtersRef.current.studentsIds,\n page: newPage ?? 1,\n limit: 10,\n orderBy,\n order,\n scoreType: scoreTypeRef.current,\n },\n true\n );\n },\n [fetchSimulatedOverview]\n );\n\n const handleContentsParamsChange = useCallback(\n (params: Record<string, unknown>) => {\n if (selectedAreaKnowledgeIdRef.current === ESSAY_AREA_ID) {\n return;\n }\n\n const newPage = params.page as number;\n const newLimit = params.limit as number;\n const sortBy = params.sortBy as string;\n const sortOrder = params.sortOrder as string;\n\n const sortByMap: Record<string, string> = {\n contentName: 'contentName',\n simulatedExamsCount: 'simulatedExamsCount',\n questionsCount: 'questionsCount',\n studentsCount: 'studentsCount',\n performance: 'correctPercentage',\n };\n\n const orderBy = sortBy\n ? (sortByMap[sortBy] ?? 'correctPercentage')\n : 'correctPercentage';\n const order = sortOrder === 'desc' ? 'desc' : 'asc';\n\n const currentSubjectId = selectedSubjectIdRef.current;\n const currentAreaKnowledgeId = selectedAreaKnowledgeIdRef.current;\n const effectiveSubjectId =\n currentSubjectId && currentSubjectId !== 'all'\n ? currentSubjectId\n : undefined;\n const effectiveAreaKnowledgeId =\n currentAreaKnowledgeId && currentAreaKnowledgeId !== 'all'\n ? currentAreaKnowledgeId\n : undefined;\n\n fetchContents({\n simulationType: 'enem-1',\n period: periodRef.current,\n subjectId: effectiveSubjectId,\n areaKnowledgeId: effectiveAreaKnowledgeId,\n schoolIds: filtersRef.current.schoolIds,\n schoolYearIds: filtersRef.current.schoolYearIds,\n classIds: filtersRef.current.classIds,\n studentsIds: filtersRef.current.studentsIds,\n page: newPage ?? 1,\n limit: newLimit ?? 10,\n orderBy,\n order,\n scoreType: scoreTypeRef.current,\n });\n },\n [fetchContents]\n );\n\n const handleStudentRowClick = useCallback((row: SimulatedStudentItem) => {\n setSelectedStudent({\n userInstitutionId: row.userInstitutionId,\n name: row.name,\n });\n setIsStudentModalOpen(true);\n }, []);\n\n const handleContentRowClick = useCallback((row: SimulatedContentItem) => {\n setSelectedContent({\n contentId: row.contentId,\n contentName: row.contentName,\n });\n setIsContentModalOpen(true);\n }, []);\n\n // === Carregamento inicial ===\n const initialLoadRef = useRef(false);\n const hasInitialLoadCompleted = useRef(false);\n\n useEffect(() => {\n // Skip if hook is disabled (e.g., not on simulated report)\n if (!enabled) return;\n if (initialLoadRef.current) return;\n initialLoadRef.current = true;\n\n // Load aggregated overview (based on profile)\n loadAggregatedOverviewData(null, null);\n // Load paginated students for table\n loadStudentsData(null, null);\n loadGeneralOverviewData();\n\n // Set after microtask to prevent scoreType effect from firing on mount\n Promise.resolve().then(() => {\n hasInitialLoadCompleted.current = true;\n });\n }, [\n enabled,\n loadStudentsData,\n loadGeneralOverviewData,\n loadAggregatedOverviewData,\n ]);\n\n // === Recarregar quando scoreType muda ===\n useEffect(() => {\n // Skip if hook is disabled\n if (!enabled) return;\n if (!hasInitialLoadCompleted.current) return;\n\n // Always reload aggregated overview\n loadAggregatedOverviewData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n\n if (simulatedViewTabRef.current === SimulatedViewTab.STUDENTS) {\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n } else {\n loadSkillsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n loadStudentsData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n }\n loadGeneralOverviewData();\n }, [\n enabled,\n scoreType,\n loadStudentsData,\n loadSkillsData,\n loadGeneralOverviewData,\n loadAggregatedOverviewData,\n ]);\n\n // === Recarregar quando aggregationType muda (perfil carregado após mount) ===\n const previousAggregationTypeRef = useRef(aggregationType);\n useEffect(() => {\n // Skip if hook is disabled\n if (!enabled) return;\n // Skip if aggregationType hasn't actually changed\n if (previousAggregationTypeRef.current === aggregationType) return;\n previousAggregationTypeRef.current = aggregationType;\n\n // Skip if initial load hasn't completed\n if (!hasInitialLoadCompleted.current) return;\n\n // Reload aggregated overview with new aggregation type\n loadAggregatedOverviewData(\n selectedAreaKnowledgeIdRef.current,\n selectedSubjectIdRef.current\n );\n }, [enabled, aggregationType, loadAggregatedOverviewData]);\n\n return {\n // Query Params\n period,\n scoreType,\n\n // Estados de Seleção\n selectedAreaKnowledgeId,\n selectedAreaRelatedIds,\n selectedSubjectId,\n simulatedViewTab,\n isEssaySelected,\n\n // Filtros\n filters,\n activeFiltersCount,\n\n // Aggregation Type\n aggregationType,\n\n // Dados da API\n generalOverview: {\n data: generalOverviewData,\n loading: generalOverviewLoading,\n error: generalOverviewError,\n },\n aggregatedOverview: {\n data: aggregatedData,\n loading: aggregatedLoading,\n isRefreshing: aggregatedRefreshing,\n error: aggregatedError,\n },\n studentsOverview: {\n data: simulatedData,\n loading: simulatedLoading,\n isRefreshing: simulatedRefreshing,\n error: simulatedError,\n },\n contentsPerformance: {\n data: contentsData,\n loading: contentsLoading,\n isRefreshing: contentsRefreshing,\n error: contentsError,\n },\n\n // Handlers\n handlePeriodChange,\n handleScoreTypeChange,\n handleAreaKnowledgeChange,\n handleSubjectChange,\n handleViewTabChange,\n handleFiltersApply,\n handleStudentsParamsChange,\n handleContentsParamsChange,\n handleStudentRowClick,\n handleContentRowClick,\n\n // Modais\n studentModal: {\n isOpen: isStudentModalOpen,\n student: selectedStudent,\n close: () => {\n setIsStudentModalOpen(false);\n setSelectedStudent(null);\n },\n },\n contentModal: {\n isOpen: isContentModalOpen,\n content: selectedContent,\n close: () => {\n setIsContentModalOpen(false);\n setSelectedContent(null);\n },\n },\n filtersModal: {\n isOpen: isFiltersModalOpen,\n open: () => setIsFiltersModalOpen(true),\n close: () => setIsFiltersModalOpen(false),\n },\n\n // Colunas das Tabelas\n studentsTableColumns,\n contentsTableColumns,\n };\n}\n","import Text from '../Text/Text';\nimport ProgressBar from '../ProgressBar/ProgressBar';\nimport { SkeletonRounded } from '../Skeleton/Skeleton';\nimport IconRender from '../IconRender/IconRender';\nimport { formatScore, hexToRgba } from '../../utils/utils';\nimport { ScoreType } from '../../types/common';\nimport {\n type GeneralOverviewSectionProps,\n type AreaKnowledgePerformance,\n type EssayPerformance,\n} from './types';\n\n/**\n * Area knowledge card component\n * Uses color and icon from API data\n */\nfunction AreaCard({\n area,\n scoreType = ScoreType.PERCENTAGE,\n}: {\n readonly area: AreaKnowledgePerformance;\n readonly scoreType?: ScoreType;\n}) {\n const bgColor = hexToRgba(area.color, 0.1);\n\n return (\n <div\n className=\"flex-1 min-w-0 flex flex-col items-center justify-center p-4 rounded-xl\"\n style={{ backgroundColor: bgColor }}\n >\n {/* Icon with rounded background */}\n <div\n className=\"w-8 h-8 flex items-center justify-center rounded-full mb-2\"\n style={{ backgroundColor: area.color }}\n >\n <IconRender iconName={area.icon} size={18} color=\"white\" />\n </div>\n\n {/* Full name (uppercase, smaller) */}\n <Text\n size=\"xs\"\n weight=\"semibold\"\n className=\"text-center uppercase text-text-700 mb-1 line-clamp-2\"\n >\n {area.name}\n </Text>\n\n {/* Score (percentage or TRI) */}\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n {formatScore(area.percentage, scoreType)}\n </Text>\n </div>\n );\n}\n\n/**\n * Essay card component\n * Displays essay performance using color and icon from API\n * Note: Essays always use percentage, TRI doesn't apply to essays\n */\nfunction EssayCard({ essay }: { readonly essay: EssayPerformance }) {\n const bgColor = hexToRgba(essay.color, 0.1);\n\n return (\n <div\n className=\"flex-1 min-w-0 flex flex-col items-center justify-center p-4 rounded-xl\"\n style={{ backgroundColor: bgColor }}\n >\n {/* Icon with rounded background */}\n <div\n className=\"w-8 h-8 flex items-center justify-center rounded-full mb-2\"\n style={{ backgroundColor: essay.color }}\n >\n <IconRender iconName={essay.icon} size={18} color=\"white\" />\n </div>\n\n {/* Name (uppercase, smaller) */}\n <Text\n size=\"xs\"\n weight=\"semibold\"\n className=\"text-center uppercase text-text-700 mb-1 line-clamp-2\"\n >\n {essay.name}\n </Text>\n\n {/* Percentage large - Essays always use percentage */}\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n {formatScore(essay.percentage, ScoreType.PERCENTAGE)}\n </Text>\n </div>\n );\n}\n\n/**\n * Skeleton loader for area cards\n */\nfunction AreaCardSkeleton() {\n return (\n <div className=\"flex-1 min-w-0 flex flex-col items-center justify-center p-4 rounded-xl bg-background-50\">\n <SkeletonRounded className=\"h-8 w-8 rounded-full mb-2\" />\n <SkeletonRounded className=\"h-3 w-16 mb-1\" />\n <SkeletonRounded className=\"h-6 w-12\" />\n </div>\n );\n}\n\n/**\n * GeneralOverviewSection - General overview section for simulated exams\n *\n * Displays overall proficiency percentage and performance by area of knowledge.\n * Uses color and icon directly from API data for each area.\n * This section is independent of areaKnowledgeId and subjectId filters -\n * it always shows consolidated data across all simulated exams.\n *\n * @example\n * ```tsx\n * const { data, loading, error, fetchOverview } = useGeneralOverview(api);\n *\n * <GeneralOverviewSection\n * data={data}\n * loading={loading}\n * error={error}\n * scoreType=\"tri\"\n * />\n * ```\n */\nexport function GeneralOverviewSection({\n data,\n loading = false,\n error = null,\n scoreType = ScoreType.PERCENTAGE,\n}: Readonly<GeneralOverviewSectionProps>) {\n if (error) {\n return (\n <div className=\"p-4 bg-error-50 border border-error-200 rounded-xl\">\n <Text size=\"sm\" className=\"text-error-700\">\n {error}\n </Text>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"space-y-4 p-5 bg-background border border-border-50 rounded-xl\">\n {/* Header */}\n <div className=\"space-y-1\">\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n Geral\n </Text>\n <SkeletonRounded className=\"h-4 w-3/4\" />\n </div>\n\n {/* Overall progress skeleton */}\n <div className=\"flex items-center gap-4\">\n <div className=\"flex-1\">\n <SkeletonRounded className=\"h-3 w-full rounded-full\" />\n </div>\n <SkeletonRounded className=\"h-6 w-12\" />\n </div>\n\n {/* Area cards skeleton */}\n <div className=\"flex gap-3\">\n {[1, 2, 3, 4, 5].map((i) => (\n <AreaCardSkeleton key={i} />\n ))}\n </div>\n </div>\n );\n }\n\n if (!data) {\n return null;\n }\n\n return (\n <div className=\"space-y-4 p-5 bg-background border border-border-50 rounded-xl\">\n {/* Header with description */}\n <div className=\"space-y-1\">\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n Geral\n </Text>\n <Text size=\"sm\" className=\"text-text-500\">\n Dados que mostram a proficiência de todos os simulados digitais já\n realizados em todas as áreas do conhecimento\n </Text>\n </div>\n\n {/* Overall progress bar */}\n <div className=\"flex items-center gap-4\">\n <div className=\"flex-1\">\n <ProgressBar\n value={\n scoreType === ScoreType.TRI\n ? data.overallPercentage / 10\n : data.overallPercentage\n }\n variant=\"green\"\n size=\"small\"\n />\n </div>\n <Text\n size=\"lg\"\n weight=\"bold\"\n className=\"text-text-950 min-w-[50px] text-right\"\n >\n {formatScore(data.overallPercentage, scoreType)}\n </Text>\n </div>\n\n {/* Area knowledge cards + Essay card - flex to fill 100% equally */}\n {(data.areas.length > 0 || data.essay) && (\n <div className=\"flex gap-3\">\n {data.areas.map((area) => (\n <AreaCard key={area.id} area={area} scoreType={scoreType} />\n ))}\n {data.essay && <EssayCard essay={data.essay} />}\n </div>\n )}\n </div>\n );\n}\n\nexport default GeneralOverviewSection;\n","import { useState, useCallback, useRef } from 'react';\nimport type { BaseApiClient } from '../../types/api';\nimport type {\n GeneralOverviewParams,\n GeneralOverviewApiResponse,\n UseGeneralOverviewState,\n UseGeneralOverviewReturn,\n} from './types';\n\n/**\n * Initial state for the hook\n */\nconst initialState: UseGeneralOverviewState = {\n data: null,\n loading: false,\n isRefreshing: false,\n error: null,\n};\n\n/**\n * Hook for fetching general overview data for simulated exams\n *\n * Receives the API client and calls the endpoint directly.\n * Endpoint: POST /performance/simulated/general-overview\n *\n * @param api - API client instance (axios-like)\n * @returns Hook state and methods\n *\n * @example\n * ```tsx\n * import api from '@/services/apiService';\n *\n * const { data, loading, error, fetchOverview } = useGeneralOverview(api);\n *\n * useEffect(() => {\n * fetchOverview({\n * period: '1_MONTH',\n * schoolIds: ['school-id'],\n * scoreType: 'tri',\n * });\n * }, []);\n * ```\n */\nexport function useGeneralOverview(\n api: BaseApiClient\n): UseGeneralOverviewReturn {\n const [state, setState] = useState<UseGeneralOverviewState>(initialState);\n const requestIdRef = useRef(0);\n\n /**\n * Fetch general overview data\n * @param params - Request parameters\n * @param refresh - If true, sets isRefreshing instead of loading\n */\n const fetchOverview = useCallback(\n async (params: GeneralOverviewParams, refresh = false) => {\n const currentRequestId = ++requestIdRef.current;\n\n // Set loading or refreshing state\n setState((prev) => ({\n ...prev,\n loading: !refresh,\n isRefreshing: refresh,\n error: null,\n }));\n\n try {\n // Build URL with optional scoreType query param\n let url = '/performance/simulated/general-overview';\n if (params.scoreType && params.scoreType !== 'percentage') {\n url += `?scoreType=${params.scoreType}`;\n }\n\n // Build request body (excludes scoreType and studentsIds)\n const body = {\n period: params.period,\n schoolIds: params.schoolIds || [],\n schoolYearIds: params.schoolYearIds || [],\n classIds: params.classIds || [],\n };\n\n const response = await api.post<GeneralOverviewApiResponse>(url, body);\n\n // Ignore stale responses\n if (currentRequestId !== requestIdRef.current) return;\n\n setState({\n data: response.data.data,\n loading: false,\n isRefreshing: false,\n error: null,\n });\n } catch (err) {\n // Ignore errors from stale requests\n if (currentRequestId !== requestIdRef.current) return;\n\n const errorMessage =\n err instanceof Error\n ? err.message\n : 'Erro ao carregar dados gerais de desempenho';\n\n console.error('Error fetching general overview:', err);\n\n setState((prev) => ({\n ...prev,\n loading: false,\n isRefreshing: false,\n error: errorMessage,\n }));\n }\n },\n [api]\n );\n\n /**\n * Reset state to initial values\n */\n const reset = useCallback(() => {\n // Invalidate any in-flight requests\n requestIdRef.current++;\n setState(initialState);\n }, []);\n\n return {\n ...state,\n fetchOverview,\n reset,\n };\n}\n\nexport default useGeneralOverview;\n","import { useState, useCallback, useRef } from 'react';\nimport type { BaseApiClient } from '../../types/api';\nimport type {\n SimulatedOverviewData,\n SimulatedOverviewParams,\n SimulatedOverviewApiResponse,\n UseSimulatedOverviewState,\n UseSimulatedOverviewReturn,\n} from './types';\nimport {\n simulationTypeToActivityFilters,\n SimulationType,\n} from '../SimulatedStudentDetailsModal/types';\nimport { getErrorMessage } from './utils';\n\n/**\n * Body type for POST endpoints\n */\ninterface SimulatedOverviewBody {\n period?: string;\n subjectId?: string;\n areaKnowledgeId?: string;\n schoolIds?: string[];\n schoolYearIds?: string[];\n classIds?: string[];\n studentsIds?: string[];\n page?: number;\n limit?: number;\n orderBy?: string;\n order?: 'asc' | 'desc';\n}\n\n/**\n * Build the API endpoint with activity filters and scoreType as query params\n */\nfunction buildEndpoint(\n simulationType: SimulationType,\n scoreType?: string\n): string {\n // Essays still use the legacy endpoint since they don't have activity filters\n if (simulationType === 'essays') {\n const params = new URLSearchParams();\n if (scoreType && scoreType !== 'percentage') {\n params.append('scoreType', scoreType);\n }\n const queryString = params.toString();\n const endpoint = '/performance/simulated/essays/students-overview';\n\n if (!queryString) {\n return endpoint;\n }\n\n return `${endpoint}?${queryString}`;\n }\n\n const activityFilters = simulationTypeToActivityFilters(simulationType);\n const params = new URLSearchParams();\n\n activityFilters.types?.forEach((t) => params.append('types', t));\n // Note: subtypes are not sent - the backend filters by areaKnowledgeId/subjectId in the body\n activityFilters.statuses?.forEach((s) => params.append('statuses', s));\n\n // Add scoreType if not default (percentage)\n if (scoreType && scoreType !== 'percentage') {\n params.append('scoreType', scoreType);\n }\n\n const queryString = params.toString();\n const endpoint = '/performance/simulated/activities/students-overview';\n\n if (!queryString) {\n return endpoint;\n }\n\n return `${endpoint}?${queryString}`;\n}\n\n/**\n * Initial state for the hook\n */\nconst initialState: UseSimulatedOverviewState = {\n data: null,\n loading: false,\n isRefreshing: false,\n error: null,\n};\n\n/**\n * Hook for fetching simulated exams overview data\n * Supports ENEM Prova 1, ENEM Prova 2, and Essays\n *\n * @param api - API client with post method\n *\n * @example\n * ```tsx\n * const { data, loading, error, fetchOverview } = useSimulatedOverview(api);\n *\n * fetchOverview({\n * simulationType: 'enem-1',\n * period: '1_MONTH',\n * subjectId: 'subject-uuid',\n * page: 1,\n * limit: 10,\n * });\n * ```\n */\nexport function useSimulatedOverview(\n api: BaseApiClient\n): UseSimulatedOverviewReturn {\n const [data, setData] = useState<SimulatedOverviewData | null>(\n initialState.data\n );\n const [loading, setLoading] = useState(initialState.loading);\n const [isRefreshing, setIsRefreshing] = useState(initialState.isRefreshing);\n const [error, setError] = useState<string | null>(initialState.error);\n\n // Track request ID to ignore stale responses\n const requestIdRef = useRef(0);\n\n /**\n * Convert params to POST body format\n */\n const paramsToBody = (\n params: SimulatedOverviewParams\n ): SimulatedOverviewBody => {\n return {\n period: params.period,\n subjectId: params.subjectId,\n areaKnowledgeId: params.areaKnowledgeId,\n schoolIds: params.schoolIds,\n schoolYearIds: params.schoolYearIds,\n classIds: params.classIds,\n studentsIds: params.studentsIds,\n page: params.page ?? 1,\n limit: params.limit ?? 10,\n orderBy: params.orderBy ?? 'name',\n order: params.order ?? 'asc',\n };\n };\n\n const fetchOverview = useCallback(\n async (params: SimulatedOverviewParams, refresh = false) => {\n const currentRequestId = ++requestIdRef.current;\n\n try {\n if (refresh) {\n setIsRefreshing(true);\n } else {\n setLoading(true);\n }\n setError(null);\n\n const endpoint = buildEndpoint(params.simulationType, params.scoreType);\n const body = paramsToBody(params);\n\n const response = await api.post<SimulatedOverviewApiResponse>(\n endpoint,\n body\n );\n\n // Ignore stale responses\n if (currentRequestId !== requestIdRef.current) return;\n\n setData(response.data.data);\n } catch (err) {\n // Ignore errors from stale requests\n if (currentRequestId !== requestIdRef.current) return;\n\n setError(\n getErrorMessage(\n err,\n 'Não foi possível carregar os dados de simulados'\n )\n );\n } finally {\n // Only update loading state for current request\n if (currentRequestId === requestIdRef.current) {\n setLoading(false);\n setIsRefreshing(false);\n }\n }\n },\n [api]\n );\n\n const reset = useCallback(() => {\n // Invalidate any in-flight requests\n requestIdRef.current++;\n setData(initialState.data);\n setLoading(initialState.loading);\n setIsRefreshing(initialState.isRefreshing);\n setError(initialState.error);\n }, []);\n\n return { data, loading, isRefreshing, error, fetchOverview, reset };\n}\n","import type { BaseApiClient } from '../../types/api';\n\n/**\n * Performance tag for simulated exams\n */\nexport enum SimulatedPerformanceTag {\n HIGHLIGHT = 'HIGHLIGHT',\n ABOVE_AVERAGE = 'ABOVE_AVERAGE',\n BELOW_AVERAGE = 'BELOW_AVERAGE',\n ATTENTION_POINT = 'ATTENTION_POINT',\n}\n\n/**\n * Tag config for display\n */\nexport enum SimulatedPerformanceTagVariant {\n SUCCESS = 'success',\n WARNING = 'warning',\n ERROR = 'error',\n INFO = 'info',\n}\n\n/**\n * Tag config for display\n */\nexport interface SimulatedPerformanceTagConfig {\n label: string;\n variant: SimulatedPerformanceTagVariant;\n}\n\n/**\n * Map performance to tag config\n */\nexport const SIMULATED_PERFORMANCE_TAG_CONFIG: Record<\n SimulatedPerformanceTag,\n SimulatedPerformanceTagConfig\n> = {\n [SimulatedPerformanceTag.HIGHLIGHT]: {\n label: 'Destaque da turma',\n variant: SimulatedPerformanceTagVariant.SUCCESS,\n },\n [SimulatedPerformanceTag.ABOVE_AVERAGE]: {\n label: 'Acima da média',\n variant: SimulatedPerformanceTagVariant.INFO,\n },\n [SimulatedPerformanceTag.BELOW_AVERAGE]: {\n label: 'Abaixo da média',\n variant: SimulatedPerformanceTagVariant.WARNING,\n },\n [SimulatedPerformanceTag.ATTENTION_POINT]: {\n label: 'Ponto de atenção',\n variant: SimulatedPerformanceTagVariant.ERROR,\n },\n};\n\n/**\n * Badge action enum for performance status indicators\n */\nexport enum PerformanceBadgeAction {\n SUCCESS = 'success',\n INFO = 'info',\n WARNING = 'warning',\n ERROR = 'error',\n}\n\n/**\n * Map performance tag to Badge action type\n */\nexport const PERFORMANCE_TAG_TO_BADGE_ACTION: Record<\n SimulatedPerformanceTag,\n PerformanceBadgeAction\n> = {\n [SimulatedPerformanceTag.HIGHLIGHT]: PerformanceBadgeAction.SUCCESS,\n [SimulatedPerformanceTag.ABOVE_AVERAGE]: PerformanceBadgeAction.INFO,\n [SimulatedPerformanceTag.BELOW_AVERAGE]: PerformanceBadgeAction.WARNING,\n [SimulatedPerformanceTag.ATTENTION_POINT]: PerformanceBadgeAction.ERROR,\n};\n\n/**\n * Simulation type used in reports\n */\nexport enum ReportSimulationType {\n ENEM_1 = 'enem-1',\n ENEM_2 = 'enem-2',\n ESSAYS = 'essays',\n}\n\n/**\n * Backward-compatible simulation type alias\n */\nexport type SimulationType = `${ReportSimulationType}`;\n\n/**\n * Student info for details modal\n */\nexport interface StudentDetailsInfo {\n studentId: string;\n institutionId: string;\n name: string;\n school: string;\n schoolYear: string;\n class: string;\n average: number;\n performance: SimulatedPerformanceTag;\n}\n\n/**\n * Subject performance item (level 1)\n */\nexport interface SubjectPerformanceItem {\n id: string;\n name: string;\n color: string | null;\n icon: string | null;\n questionsCount: number;\n performance: {\n correct: number;\n incorrect: number;\n correctPercentage: number;\n };\n}\n\n/**\n * Content performance item (level 2)\n */\nexport interface StudentContentPerformanceItem {\n contentId: string;\n contentName: string;\n bnccCode: string | null;\n questionsCount: number;\n performance: {\n correct: number;\n incorrect: number;\n correctPercentage: number;\n };\n}\n\n/**\n * Response data when subjectId is null (list of subjects)\n */\nexport interface StudentSubjectsData {\n student: StudentDetailsInfo;\n subjects: SubjectPerformanceItem[];\n page: number;\n limit: number;\n total: number;\n}\n\n/**\n * Response data when subjectId is provided (list of contents)\n */\nexport interface StudentContentsData {\n student: StudentDetailsInfo;\n subject: {\n id: string;\n name: string;\n };\n contents: StudentContentPerformanceItem[];\n page: number;\n limit: number;\n total: number;\n}\n\n/**\n * Union type for student details response\n */\nexport type StudentDetailsData = StudentSubjectsData | StudentContentsData;\n\n/**\n * Type guard to check if data is StudentSubjectsData\n */\nexport function isStudentSubjectsData(\n data: StudentDetailsData\n): data is StudentSubjectsData {\n return 'subjects' in data;\n}\n\n/**\n * Type guard to check if data is StudentContentsData\n */\nexport function isStudentContentsData(\n data: StudentDetailsData\n): data is StudentContentsData {\n return 'contents' in data;\n}\n\n/**\n * Activity filters for endpoint\n */\nexport interface ActivityFilters {\n types?: string[];\n subtypes?: string[];\n statuses?: string[];\n}\n\n/**\n * Convert SimulationType to ActivityFilters\n */\nexport function simulationTypeToActivityFilters(\n simulationType: SimulationType\n): ActivityFilters {\n switch (simulationType) {\n case ReportSimulationType.ENEM_1:\n return {\n types: ['SIMULADO'],\n subtypes: ['ENEM_PROVA_1'],\n };\n case ReportSimulationType.ENEM_2:\n return {\n types: ['SIMULADO'],\n subtypes: ['ENEM_PROVA_2'],\n };\n default:\n return {};\n }\n}\n\n/**\n * Parameters for fetching student details\n */\nexport interface StudentDetailsParams {\n simulationType: SimulationType;\n userInstitutionId: string;\n period: string;\n subjectId?: string | null;\n page?: number;\n limit?: number;\n}\n\n/**\n * API response structure\n */\nexport interface StudentDetailsApiResponse {\n message: string;\n data: StudentDetailsData;\n}\n\n/**\n * Hook state\n */\nexport interface UseSimulatedStudentDetailsState {\n data: StudentDetailsData | null;\n loading: boolean;\n error: string | null;\n}\n\n/**\n * Hook return type\n */\nexport interface UseSimulatedStudentDetailsReturn extends UseSimulatedStudentDetailsState {\n fetchDetails: (params: StudentDetailsParams) => Promise<void>;\n reset: () => void;\n}\n\n/**\n * Modal props\n */\nexport interface SimulatedStudentDetailsModalProps {\n readonly api: BaseApiClient;\n readonly isOpen: boolean;\n readonly onClose: () => void;\n readonly simulationType: SimulationType;\n readonly userInstitutionId: string | null;\n readonly studentName?: string;\n readonly period: string;\n}\n","import type {\n AggregatedOverviewData,\n StudentsOnlyOverviewData,\n ClassesOverviewData,\n MunicipalitiesOverviewData,\n OverviewAggregationType,\n} from './types';\n\n/**\n * Extract error message from an unknown error\n *\n * @param err - The caught error\n * @param fallbackMessage - Default message if error is not an Error instance\n * @returns The error message string\n */\nexport function getErrorMessage(err: unknown, fallbackMessage: string): string {\n return err instanceof Error ? err.message : fallbackMessage;\n}\n\n/**\n * Type guard to check if data is StudentsOnlyOverviewData\n * Verifies both type parameter and data shape (absence of class/municipality-specific fields)\n */\nexport function isStudentsData(\n data: AggregatedOverviewData | null,\n type: OverviewAggregationType\n): data is StudentsOnlyOverviewData {\n return (\n type === 'students' &&\n data !== null &&\n !('totalClasses' in data) &&\n !('totalMunicipalities' in data)\n );\n}\n\n/**\n * Type guard to check if data is ClassesOverviewData\n * Verifies both type parameter and data shape (presence of totalClasses)\n */\nexport function isClassesData(\n data: AggregatedOverviewData | null,\n type: OverviewAggregationType\n): data is ClassesOverviewData {\n return type === 'classes' && data !== null && 'totalClasses' in data;\n}\n\n/**\n * Type guard to check if data is MunicipalitiesOverviewData\n * Verifies both type parameter and data shape (presence of totalMunicipalities)\n */\nexport function isMunicipalitiesData(\n data: AggregatedOverviewData | null,\n type: OverviewAggregationType\n): data is MunicipalitiesOverviewData {\n return (\n type === 'municipalities' && data !== null && 'totalMunicipalities' in data\n );\n}\n","import { useState, useCallback, useRef } from 'react';\nimport type { BaseApiClient } from '../../types/api';\nimport type {\n AggregatedOverviewData,\n AggregatedOverviewParams,\n StudentsOverviewApiResponse,\n ClassesOverviewApiResponse,\n MunicipalitiesOverviewApiResponse,\n UseAggregatedOverviewState,\n UseAggregatedOverviewReturn,\n OverviewAggregationType,\n} from './types';\nimport {\n simulationTypeToActivityFilters,\n SimulationType,\n} from '../SimulatedStudentDetailsModal/types';\nimport { getErrorMessage } from './utils';\n\n/**\n * Body type for POST endpoints\n */\ninterface AggregatedOverviewBody {\n period?: string;\n subjectId?: string;\n areaKnowledgeId?: string;\n schoolIds?: string[];\n schoolYearIds?: string[];\n classIds?: string[];\n studentsIds?: string[];\n}\n\n/**\n * Build the API endpoint based on aggregation type\n */\nfunction buildEndpoint(\n aggregationType: OverviewAggregationType,\n simulationType: SimulationType,\n scoreType?: string\n): string {\n // Essays use the legacy endpoint\n if (simulationType === 'essays') {\n return '/performance/simulated/essays/students-overview';\n }\n\n const activityFilters = simulationTypeToActivityFilters(simulationType);\n const params = new URLSearchParams();\n\n // Add aggregationType as query param (unified endpoint)\n params.append('aggregationType', aggregationType);\n\n activityFilters.types?.forEach((t) => params.append('types', t));\n activityFilters.statuses?.forEach((s) => params.append('statuses', s));\n\n if (scoreType && scoreType !== 'percentage') {\n params.append('scoreType', scoreType);\n }\n\n const basePath = '/performance/simulated/activities/overview';\n return `${basePath}?${params.toString()}`;\n}\n\n/**\n * Initial state for the hook\n */\nconst initialState: UseAggregatedOverviewState = {\n data: null,\n loading: false,\n isRefreshing: false,\n error: null,\n};\n\n/**\n * Hook for fetching aggregated overview data (without students list)\n * Supports different aggregation types: students, classes, municipalities\n *\n * @param api - API client with post method\n *\n * @example\n * ```tsx\n * const { data, loading, error, fetchOverview } = useAggregatedOverview(api);\n *\n * // For students overview\n * fetchOverview({\n * aggregationType: 'students',\n * simulationType: 'enem-1',\n * period: '1_MONTH',\n * });\n *\n * // For classes overview (UNIT_MANAGER)\n * fetchOverview({\n * aggregationType: 'classes',\n * simulationType: 'enem-1',\n * period: '1_MONTH',\n * });\n *\n * // For municipalities overview (REGIONAL_MANAGER)\n * fetchOverview({\n * aggregationType: 'municipalities',\n * simulationType: 'enem-1',\n * period: '1_MONTH',\n * });\n * ```\n */\nexport function useAggregatedOverview(\n api: BaseApiClient\n): UseAggregatedOverviewReturn {\n const [data, setData] = useState<AggregatedOverviewData | null>(\n initialState.data\n );\n const [loading, setLoading] = useState(initialState.loading);\n const [isRefreshing, setIsRefreshing] = useState(initialState.isRefreshing);\n const [error, setError] = useState<string | null>(initialState.error);\n\n // Track request ID to ignore stale responses\n const requestIdRef = useRef(0);\n\n /**\n * Convert params to POST body format\n */\n const paramsToBody = (\n params: AggregatedOverviewParams\n ): AggregatedOverviewBody => {\n return {\n period: params.period,\n subjectId: params.subjectId,\n areaKnowledgeId: params.areaKnowledgeId,\n schoolIds: params.schoolIds,\n schoolYearIds: params.schoolYearIds,\n classIds: params.classIds,\n studentsIds: params.studentsIds,\n };\n };\n\n const fetchOverview = useCallback(\n async (params: AggregatedOverviewParams, refresh = false) => {\n const currentRequestId = ++requestIdRef.current;\n\n try {\n if (refresh) {\n setIsRefreshing(true);\n } else {\n setLoading(true);\n }\n setError(null);\n\n const endpoint = buildEndpoint(\n params.aggregationType,\n params.simulationType,\n params.scoreType\n );\n const body = paramsToBody(params);\n\n let responseData: AggregatedOverviewData;\n\n switch (params.aggregationType) {\n case 'classes': {\n const response = await api.post<ClassesOverviewApiResponse>(\n endpoint,\n body\n );\n responseData = response.data.data;\n break;\n }\n case 'municipalities': {\n const response = await api.post<MunicipalitiesOverviewApiResponse>(\n endpoint,\n body\n );\n responseData = response.data.data;\n break;\n }\n case 'students':\n default: {\n const response = await api.post<StudentsOverviewApiResponse>(\n endpoint,\n body\n );\n responseData = response.data.data;\n break;\n }\n }\n\n // Ignore stale responses\n if (currentRequestId !== requestIdRef.current) return;\n\n setData(responseData);\n } catch (err) {\n // Ignore errors from stale requests\n if (currentRequestId !== requestIdRef.current) return;\n\n setError(\n getErrorMessage(err, 'Não foi possível carregar os dados de overview')\n );\n } finally {\n // Only update loading state for current request\n if (currentRequestId === requestIdRef.current) {\n setLoading(false);\n setIsRefreshing(false);\n }\n }\n },\n [api]\n );\n\n const reset = useCallback(() => {\n // Invalidate any in-flight requests\n requestIdRef.current++;\n setData(initialState.data);\n setLoading(initialState.loading);\n setIsRefreshing(initialState.isRefreshing);\n setError(initialState.error);\n }, []);\n\n return { data, loading, isRefreshing, error, fetchOverview, reset };\n}\n","import { useState, useCallback, useRef } from 'react';\nimport {\n ReportSimulationType,\n simulationTypeToActivityFilters,\n type SimulationType,\n} from '../SimulatedStudentDetailsModal/types';\nimport type { BaseApiClient } from '../../types/api';\nimport type {\n SimulatedContentsParams,\n ContentsPerformanceData,\n ContentsPerformanceApiResponse,\n UseSimulatedContentsReturn,\n} from './types';\n\n/**\n * Build the API endpoint with activity filters and scoreType as query params\n */\nfunction buildEndpoint(\n simulationType: SimulationType,\n scoreType?: 'percentage' | 'tri'\n): string | null {\n // Essays don't have contents/skills\n if (simulationType === ReportSimulationType.ESSAYS) {\n return null;\n }\n\n const activityFilters = simulationTypeToActivityFilters(simulationType);\n const params = new URLSearchParams();\n\n activityFilters.types?.forEach((t) => params.append('types', t));\n // Note: subtypes are not sent - filtering is done via subjectId/areaKnowledgeId in the body\n activityFilters.statuses?.forEach((s) => params.append('statuses', s));\n\n if (scoreType && scoreType !== 'percentage') {\n params.append('scoreType', scoreType);\n }\n\n const queryString = params.toString();\n const endpoint = '/performance/simulated/activities/contents-performance';\n\n if (!queryString) {\n return endpoint;\n }\n\n return `${endpoint}?${queryString}`;\n}\n\n/**\n * Hook for fetching contents/skills performance in simulated exams\n *\n * @param api - API client with post method\n *\n * @example\n * ```tsx\n * const { data, loading, fetchContents } = useSimulatedContents(api);\n *\n * useEffect(() => {\n * fetchContents({\n * simulationType: 'enem-1',\n * period: 'ONE_MONTH',\n * page: 1,\n * limit: 10,\n * });\n * }, [fetchContents]);\n * ```\n */\nexport function useSimulatedContents(\n api: BaseApiClient\n): UseSimulatedContentsReturn {\n const [data, setData] = useState<ContentsPerformanceData | null>(null);\n const [loading, setLoading] = useState(false);\n const [isRefreshing, setIsRefreshing] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const isFirstLoad = useRef(true);\n const latestRequestId = useRef(0);\n const isCurrentRequest = (requestId: number) =>\n requestId === latestRequestId.current;\n\n const buildRequestBody = (params: SimulatedContentsParams) => ({\n period: params.period,\n subjectId:\n params.subjectId && params.subjectId !== 'all'\n ? params.subjectId\n : undefined,\n areaKnowledgeId:\n params.areaKnowledgeId && params.areaKnowledgeId !== 'all'\n ? params.areaKnowledgeId\n : undefined,\n schoolIds: params.schoolIds,\n schoolYearIds: params.schoolYearIds,\n classIds: params.classIds,\n studentsIds: params.studentsIds,\n page: params.page ?? 1,\n limit: params.limit ?? 10,\n orderBy: params.orderBy ?? 'correctPercentage',\n order: params.order ?? 'asc',\n });\n\n const handleNoEndpoint = (requestId: number) => {\n if (!isCurrentRequest(requestId)) {\n return;\n }\n setData(null);\n setError(null);\n setLoading(false);\n setIsRefreshing(false);\n isFirstLoad.current = true;\n };\n\n const startRequestState = (requestId: number, refresh: boolean) => {\n if (!isCurrentRequest(requestId)) {\n return;\n }\n if (isFirstLoad.current && !refresh) {\n setLoading(true);\n } else {\n setIsRefreshing(true);\n }\n setError(null);\n };\n\n const handleSuccess = (\n requestId: number,\n response: { data: ContentsPerformanceApiResponse }\n ) => {\n if (!isCurrentRequest(requestId)) {\n return;\n }\n setData(response.data.data);\n isFirstLoad.current = false;\n };\n\n const handleError = (requestId: number, err: unknown) => {\n if (!isCurrentRequest(requestId)) {\n return;\n }\n const errorMessage =\n err instanceof Error\n ? err.message\n : 'Erro ao carregar dados de habilidades';\n setError(errorMessage);\n setData(null);\n };\n\n const finishRequestState = (requestId: number) => {\n if (!isCurrentRequest(requestId)) {\n return;\n }\n setLoading(false);\n setIsRefreshing(false);\n };\n\n const fetchContents = useCallback(\n async (params: SimulatedContentsParams, refresh = false) => {\n const localRequestId = ++latestRequestId.current;\n const endpoint = buildEndpoint(params.simulationType, params.scoreType);\n\n // Essays don't have contents\n if (!endpoint) {\n handleNoEndpoint(localRequestId);\n return;\n }\n\n startRequestState(localRequestId, refresh);\n\n try {\n const response = await api.post<ContentsPerformanceApiResponse>(\n endpoint,\n buildRequestBody(params)\n );\n handleSuccess(localRequestId, response);\n } catch (err) {\n handleError(localRequestId, err);\n } finally {\n finishRequestState(localRequestId);\n }\n },\n [api]\n );\n\n const reset = useCallback(() => {\n setData(null);\n setLoading(false);\n setIsRefreshing(false);\n setError(null);\n isFirstLoad.current = true;\n }, []);\n\n return {\n data,\n loading,\n isRefreshing,\n error,\n fetchContents,\n reset,\n };\n}\n","import { useMemo } from 'react';\nimport Select, {\n SelectTrigger,\n SelectValue,\n SelectContent,\n SelectItem,\n} from '../Select/Select';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\nimport { GridFourIcon } from '@phosphor-icons/react/dist/csr/GridFour';\nimport type { AreaKnowledgeSelectorProps } from './types';\nimport type { AreaKnowledgePerformance } from '../GeneralOverviewSection/types';\nimport { ESSAY_AREA_ID } from './types';\n\ninterface SelectItemData {\n id: string;\n name: string;\n color?: string;\n}\n\n/**\n * AreaKnowledgeSelector - Select dropdown for knowledge areas\n *\n * Allows filtering by area of knowledge (Área de Conhecimento).\n * Shows \"Todos\" option plus all available areas from the general overview.\n * Optionally includes \"Redação\" (essay) option at the end.\n *\n * @example\n * ```tsx\n * <AreaKnowledgeSelector\n * areas={generalOverviewData?.areas || []}\n * selectedAreaId={selectedAreaKnowledgeId}\n * onAreaChange={handleAreaKnowledgeChange}\n * loading={generalOverviewLoading}\n * />\n * ```\n */\nexport function AreaKnowledgeSelector({\n areas,\n selectedAreaId,\n onAreaChange,\n loading = false,\n label = 'Área de conhecimento',\n includeEssay = true,\n}: AreaKnowledgeSelectorProps) {\n // Build select items with \"Todos\" option and optionally \"Redação\" at the end\n const selectItems = useMemo((): SelectItemData[] => {\n const allOption: SelectItemData = {\n id: 'all',\n name: 'Todos',\n };\n\n const areaItems: SelectItemData[] = areas.map(\n (area: AreaKnowledgePerformance) => ({\n id: area.id,\n name: area.name,\n color: area.color,\n })\n );\n\n const items = [allOption, ...areaItems];\n\n if (includeEssay) {\n const essayOption: SelectItemData = {\n id: ESSAY_AREA_ID,\n name: 'Redação',\n };\n items.push(essayOption);\n }\n\n return items;\n }, [areas, includeEssay]);\n\n // Handle value change\n const handleValueChange = (value: string) => {\n if (value === 'all') {\n onAreaChange(null);\n } else {\n onAreaChange(value);\n }\n };\n\n const effectiveValue = selectedAreaId || 'all';\n\n return (\n <div className=\"space-y-2\">\n <Text size=\"sm\" weight=\"medium\" className=\"text-text-700\">\n {label}\n </Text>\n <div className=\"relative\">\n <Select\n defaultValue=\"all\"\n value={effectiveValue}\n onValueChange={handleValueChange}\n >\n <SelectTrigger\n className={cn('w-full', loading && 'opacity-50')}\n disabled={loading}\n >\n <SelectValue placeholder=\"Selecione uma área\" />\n </SelectTrigger>\n <SelectContent>\n {selectItems.map((item: SelectItemData) => {\n let itemIcon = null;\n\n if (item.id === 'all') {\n itemIcon = (\n <span className=\"w-[21px] h-[21px] flex items-center justify-center\">\n <GridFourIcon\n size={17}\n weight=\"bold\"\n className=\"text-gray-600\"\n />\n </span>\n );\n } else if (item.color) {\n itemIcon = (\n <span\n className=\"w-[21px] h-[21px] flex items-center justify-center rounded-sm\"\n style={{ backgroundColor: `${item.color}20` }}\n >\n <span\n className=\"w-2 h-2 rounded-full\"\n style={{ backgroundColor: item.color }}\n />\n </span>\n );\n }\n\n return (\n <SelectItem key={item.id} value={item.id} disabled={loading}>\n <div className=\"flex items-center gap-2\">\n {itemIcon}\n <span className=\"whitespace-nowrap\">{item.name}</span>\n </div>\n </SelectItem>\n );\n })}\n </SelectContent>\n </Select>\n </div>\n </div>\n );\n}\n\nexport default AreaKnowledgeSelector;\n","// Reuse types from GeneralOverviewSection\nimport type { AreaKnowledgePerformance } from '../GeneralOverviewSection/types';\n\n/**\n * Special ID for essay (Redação) area\n */\nexport const ESSAY_AREA_ID = 'essay';\n\n/**\n * Props for AreaKnowledgeSelector component\n */\nexport interface AreaKnowledgeSelectorProps {\n /** List of areas from general overview (only id and name are used) */\n readonly areas: AreaKnowledgePerformance[];\n /** Currently selected area ID (null means \"Todos\") */\n readonly selectedAreaId: string | null;\n /** Callback when area changes */\n readonly onAreaChange: (areaId: string | null) => void;\n /** Loading state */\n readonly loading?: boolean;\n /** Label text (default: \"Área de conhecimento\") */\n readonly label?: string;\n /** Include essay option at the end (default: true) */\n readonly includeEssay?: boolean;\n}\n","import { useEffect, useState, useCallback, type ReactNode } from 'react';\nimport ReportDetailModal from '../ReportDetailModal/ReportDetailModal';\nimport Text from '../Text/Text';\nimport ProgressBar from '../ProgressBar/ProgressBar';\nimport Badge from '../Badge/Badge';\nimport { ArrowLeftIcon } from '@phosphor-icons/react/dist/csr/ArrowLeft';\nimport { CaretRightIcon } from '@phosphor-icons/react/dist/csr/CaretRight';\nimport { useSimulatedStudentDetails } from './useSimulatedStudentDetails';\nimport { formatPercentageRounded } from '../../utils/utils';\nimport { downloadExcel } from '../../utils/exportExcel';\nimport { formatDateForFileName } from '../../utils/exportFormat';\nimport { buildSimulatedStudentDetailsSheets } from './exportSheets';\nimport {\n isStudentSubjectsData,\n SIMULATED_PERFORMANCE_TAG_CONFIG,\n PERFORMANCE_TAG_TO_BADGE_ACTION,\n type SimulatedStudentDetailsModalProps,\n type SubjectPerformanceItem,\n type StudentContentPerformanceItem,\n} from './types';\nimport Button from '../Button/Button';\n\n/**\n * Prefixo do arquivo exportado, em PDF e em XLSX. Recebe a data do dia.\n *\n * Sem o nome do estudante nem o da matéria de propósito: os dois trazem\n * acentos, espaços e às vezes pontuação, e um nome de arquivo pede\n * transliteração — que não existe nesta lib e que não vale a pena inventar\n * aqui. É o mesmo prefixo nos dois níveis da cascata; quem diz de qual nível é\n * o arquivo são as abas da planilha (o nome da segunda muda) e o título\n * impresso no PDF.\n */\nconst EXPORT_FILE_PREFIX = 'desempenho-simulado';\n\n/**\n * Modal for displaying student performance details in simulated exams\n * Supports cascading navigation: Subjects (level 1) -> Contents (level 2)\n *\n * Exportável: monta sobre o `ReportDetailModal`, então traz o botão \"Baixar\n * relatório\" com PDF (impressão só deste modal) e XLSX. As duas saídas usam o\n * dado que ESTE componente já buscou por `useSimulatedStudentDetails` — nenhuma\n * requisição a mais é disparada para exportar — e a planilha espelha os blocos\n * da tela aba a aba (veja `exportSheets.ts`).\n *\n * CASCATA: a planilha leva o nível VISÍVEL, um só. O hook guarda um `data`\n * único e o troca a cada navegação, então o outro nível não está em memória;\n * levá-lo junto exigiria requisições que a tela não fez. Ver a justificativa\n * completa em `exportSheets.ts`.\n *\n * ESTADOS DE CARREGANDO, ERRO E SEM-DADO: o botão continua lá, porque é o\n * `ReportDetailModal` que o desenha e ele não tem como escondê-lo. O PDF sai com\n * o que estiver na tela (a mensagem de carregando, a de erro ou a de\n * \"Nenhum dado encontrado\") e o XLSX sai com as duas abas só de cabeçalho, já\n * que `data` é `null` — nenhum dos dois estoura.\n *\n * O consumidor precisa importar `analytica-frontend-lib/print.css`: sem as\n * regras `@media print` o PDF sai com a página inteira em vez do modal.\n */\nexport function SimulatedStudentDetailsModal({\n api,\n isOpen,\n onClose,\n simulationType,\n userInstitutionId,\n studentName,\n period,\n}: SimulatedStudentDetailsModalProps) {\n const { data, loading, error, fetchDetails, reset } =\n useSimulatedStudentDetails(api);\n const [selectedSubject, setSelectedSubject] = useState<{\n id: string;\n name: string;\n } | null>(null);\n\n // Fetch subjects when modal opens\n useEffect(() => {\n if (isOpen && userInstitutionId) {\n fetchDetails({\n simulationType,\n userInstitutionId,\n period,\n subjectId: null,\n });\n }\n }, [isOpen, userInstitutionId, simulationType, period, fetchDetails]);\n\n // Reset state when modal closes\n useEffect(() => {\n if (!isOpen) {\n reset();\n setSelectedSubject(null);\n }\n }, [isOpen, reset]);\n\n // Handle subject click (navigate to level 2)\n const handleSubjectClick = useCallback(\n (subject: SubjectPerformanceItem) => {\n if (!userInstitutionId) return;\n\n setSelectedSubject({ id: subject.id, name: subject.name });\n fetchDetails({\n simulationType,\n userInstitutionId,\n period,\n subjectId: subject.id,\n });\n },\n [userInstitutionId, simulationType, period, fetchDetails]\n );\n\n // Handle back navigation (return to level 1)\n const handleBack = useCallback(() => {\n if (!userInstitutionId) return;\n\n setSelectedSubject(null);\n fetchDetails({\n simulationType,\n userInstitutionId,\n period,\n subjectId: null,\n });\n }, [userInstitutionId, simulationType, period, fetchDetails]);\n\n // O nome que o TÍTULO do modal exibe no nível 2 — e, por isso, o que a aba de\n // resumo da planilha carrega. Sai do estado local, não de\n // `StudentContentsData.subject`, para que o arquivo diga o mesmo que a tela.\n const selectedSubjectName = selectedSubject?.name ?? null;\n\n const fileName = `${EXPORT_FILE_PREFIX}-${formatDateForFileName(new Date())}`;\n\n const handleDownloadExcel = useCallback(() => {\n downloadExcel(\n fileName,\n buildSimulatedStudentDetailsSheets(data, selectedSubjectName)\n );\n }, [fileName, data, selectedSubjectName]);\n\n // Build modal title with back button when in level 2\n const modalTitle = selectedSubject ? (\n <div className=\"flex items-center gap-2\">\n {/* `data-print-hide`: navegar é controle de tela, não conteúdo do\n relatório, e no caminho PDF este botão está DENTRO da região impressa.\n Fica no próprio <Button> porque ele É o item do flex — escondido, o\n `gap-2` não separa mais nada, que é o que o contrato do print.css\n quer evitar. O nome da matéria ao lado continua saindo no papel. */}\n <Button\n onClick={handleBack}\n variant=\"raw\"\n className=\"p-1 hover:bg-background-100 rounded-md transition-colors\"\n aria-label=\"Voltar para lista de componentes curriculares\"\n data-print-hide\n >\n <ArrowLeftIcon size={20} className=\"text-text-600\" />\n </Button>\n <Text>{selectedSubject.name}</Text>\n </div>\n ) : (\n `Desempenho de ${studentName || 'Estudante'}`\n );\n\n const renderStatusMessage = (message: ReactNode, className: string) => (\n <div className=\"flex items-center justify-center py-8\">\n <Text size=\"sm\" className={className}>\n {message}\n </Text>\n </div>\n );\n\n const renderDetailsContent = (\n details: NonNullable<typeof data>\n ): ReactNode => {\n if (isStudentSubjectsData(details)) {\n if (details.subjects.length > 0) {\n return details.subjects.map((subject) => (\n <SubjectItem\n key={subject.id}\n subject={subject}\n onClick={() => handleSubjectClick(subject)}\n questionsLabel=\"questões\"\n />\n ));\n }\n\n return (\n <Text size=\"sm\" className=\"text-text-500 text-center py-4\">\n Nenhum componente curricular encontrado\n </Text>\n );\n }\n\n if (details.contents.length > 0) {\n return details.contents.map((content) => (\n <ContentItem\n key={content.contentId}\n content={content}\n questionsLabel=\"questões\"\n ofLabel=\"de\"\n />\n ));\n }\n\n return (\n <Text size=\"sm\" className=\"text-text-500 text-center py-4\">\n Nenhuma habilidade encontrada\n </Text>\n );\n };\n\n // Os quatro estados da tela dividem a MESMA casca de modal: antes cada um\n // devolvia o seu próprio `Modal`, o que agora daria quatro `ReportDetailModal`\n // e, com eles, um botão de download que aparece e some conforme o dado chega.\n const renderModalContent = (): ReactNode => {\n if (loading) {\n return renderStatusMessage('Carregando...', 'text-text-500');\n }\n\n if (error) {\n return renderStatusMessage(error, 'text-error-500');\n }\n\n if (!data) {\n return renderStatusMessage('Nenhum dado encontrado', 'text-text-500');\n }\n\n return (\n <div className=\"flex flex-col gap-4\">\n {/* Student header card */}\n <div className=\"flex items-center justify-between p-4 bg-background-50 rounded-xl\">\n <div className=\"flex flex-col gap-1\">\n <Text size=\"md\" weight=\"semibold\" className=\"text-text-950\">\n {data.student.name}\n </Text>\n <Text size=\"sm\" className=\"text-text-500\">\n {data.student.school} - {data.student.class}\n </Text>\n </div>\n <div className=\"flex flex-col items-end gap-1\">\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n {formatPercentageRounded(data.student.average)}\n </Text>\n <Badge\n variant=\"solid\"\n action={PERFORMANCE_TAG_TO_BADGE_ACTION[data.student.performance]}\n size=\"small\"\n >\n {SIMULATED_PERFORMANCE_TAG_CONFIG[data.student.performance].label}\n </Badge>\n </div>\n </div>\n\n {/* List of subjects or contents */}\n <div className=\"flex flex-col gap-2 max-h-[400px] overflow-y-auto\">\n {renderDetailsContent(data)}\n </div>\n </div>\n );\n };\n\n return (\n <ReportDetailModal\n isOpen={isOpen}\n onClose={onClose}\n title={modalTitle}\n size=\"lg\"\n // Só `fileName`: o PDF é a impressão deste modal, e o `ReportDetailModal`\n // já a faz. Ligar `onDownloadPdf` a um `useReportPrint` local somaria uma\n // segunda impressão — o callback SUBSTITUI a embutida, então o usuário\n // veria dois diálogos.\n fileName={fileName}\n onDownloadExcel={handleDownloadExcel}\n >\n {renderModalContent()}\n </ReportDetailModal>\n );\n}\n\n/**\n * Subject item component (level 1)\n */\nfunction SubjectItem({\n subject,\n onClick,\n questionsLabel,\n}: Readonly<{\n subject: SubjectPerformanceItem;\n onClick: () => void;\n questionsLabel: string;\n}>) {\n return (\n <button\n onClick={onClick}\n className=\"flex items-center gap-4 p-4 bg-background border border-border-50 rounded-xl hover:bg-background-50 transition-colors text-left w-full\"\n /* A linha é `<button>` porque desce para o nível 2, mas o TEXTO dela é o\n conteúdo do relatório: nome da matéria, questões e percentual. Por isso\n ela NÃO leva `data-print-hide` — escondê-la apagaria a lista de matérias\n do PDF. O testid existe para o invariante de impressão poder declará-la\n como impressa de propósito (veja `testing/printRegionInvariant.ts`); sem\n ele a linha não tem atributo nenhum que a distinga de um controle. */\n data-testid=\"subject-performance-row\"\n >\n {/* Subject color indicator */}\n {subject.color && (\n <div\n className=\"w-3 h-3 rounded-full flex-shrink-0\"\n style={{ backgroundColor: subject.color }}\n />\n )}\n\n {/* Subject info */}\n <div className=\"flex-1 min-w-0\">\n <Text size=\"sm\" weight=\"semibold\" className=\"text-text-950 truncate\">\n {subject.name}\n </Text>\n <Text size=\"xs\" className=\"text-text-500\">\n {subject.questionsCount} {questionsLabel}\n </Text>\n </div>\n\n {/* Progress bar */}\n <div className=\"w-32 flex-shrink-0\">\n <ProgressBar\n value={subject.performance.correctPercentage}\n variant=\"green\"\n size=\"small\"\n showPercentage\n />\n </div>\n\n {/* Arrow indicator */}\n <CaretRightIcon size={16} className=\"text-text-400 flex-shrink-0\" />\n </button>\n );\n}\n\n/**\n * Content item component (level 2)\n */\nfunction ContentItem({\n content,\n questionsLabel,\n ofLabel,\n}: Readonly<{\n content: StudentContentPerformanceItem;\n questionsLabel: string;\n ofLabel: string;\n}>) {\n return (\n <div className=\"flex items-center gap-4 p-4 bg-background border border-border-50 rounded-xl\">\n {/* Content info */}\n <div className=\"flex-1 min-w-0\">\n <Text size=\"sm\" weight=\"semibold\" className=\"text-text-950 truncate\">\n {content.contentName}\n </Text>\n <div className=\"flex items-center gap-2\">\n {content.bnccCode && (\n <Text size=\"xs\" className=\"text-primary-600\">\n {content.bnccCode}\n </Text>\n )}\n <Text size=\"xs\" className=\"text-text-500\">\n {content.questionsCount} {questionsLabel}\n </Text>\n </div>\n </div>\n\n {/* Progress bar */}\n <div className=\"w-32 flex-shrink-0\">\n <ProgressBar\n value={content.performance.correctPercentage}\n variant=\"green\"\n size=\"small\"\n showPercentage\n />\n </div>\n\n {/* Hit count */}\n <div className=\"flex items-center gap-1 flex-shrink-0\">\n <Text size=\"sm\" weight=\"semibold\" className=\"text-success-600\">\n {content.performance.correct}\n </Text>\n <Text size=\"xs\" className=\"text-text-500 whitespace-nowrap\">\n {ofLabel} {content.questionsCount}\n </Text>\n </div>\n </div>\n );\n}\n","import { useState, useCallback, useRef } from 'react';\nimport type { BaseApiClient } from '../../types/api';\nimport {\n ReportSimulationType,\n simulationTypeToActivityFilters,\n type SimulationType,\n type StudentDetailsData,\n type StudentDetailsParams,\n type StudentDetailsApiResponse,\n type UseSimulatedStudentDetailsReturn,\n} from './types';\nimport { paramsToBody } from './utils';\n\n/**\n * Build the API endpoint with activity filters as query params\n */\nfunction buildEndpoint(simulationType: SimulationType): string {\n if (simulationType === ReportSimulationType.ESSAYS) {\n return '/performance/simulated/essays/student-details';\n }\n\n const activityFilters = simulationTypeToActivityFilters(simulationType);\n const params = new URLSearchParams();\n\n activityFilters.types?.forEach((t) => params.append('types', t));\n // Note: subtypes are not sent - filtering is done via subjectId in the body\n activityFilters.statuses?.forEach((s) => params.append('statuses', s));\n\n const queryString = params.toString();\n const endpoint = '/performance/simulated/activities/student-details';\n\n if (!queryString) {\n return endpoint;\n }\n\n return `${endpoint}?${queryString}`;\n}\n\n/**\n * Hook for fetching student details in simulated exams\n * Supports cascading navigation: Subjects (level 1) -> Contents (level 2)\n *\n * @param api - API client with post method\n *\n * @example\n * ```tsx\n * const { data, loading, error, fetchDetails, reset } = useSimulatedStudentDetails(api);\n *\n * // Fetch subjects list (level 1)\n * fetchDetails({\n * simulationType: 'enem-1',\n * userInstitutionId: 'institution-uuid',\n * period: '1_MONTH',\n * });\n *\n * // Fetch contents list (level 2)\n * fetchDetails({\n * simulationType: 'enem-1',\n * userInstitutionId: 'institution-uuid',\n * period: '1_MONTH',\n * subjectId: 'subject-uuid',\n * });\n * ```\n */\nexport function useSimulatedStudentDetails(\n api: BaseApiClient\n): UseSimulatedStudentDetailsReturn {\n const [data, setData] = useState<StudentDetailsData | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n // Track request ID to ignore stale responses\n const requestIdRef = useRef(0);\n\n const fetchDetails = useCallback(\n async (params: StudentDetailsParams) => {\n const currentRequestId = ++requestIdRef.current;\n\n try {\n setLoading(true);\n setError(null);\n setData(null);\n\n const endpoint = buildEndpoint(params.simulationType);\n const body = paramsToBody(params);\n\n const response = await api.post<StudentDetailsApiResponse>(\n endpoint,\n body\n );\n\n // Ignore stale responses\n if (currentRequestId !== requestIdRef.current) return;\n\n setData(response.data.data);\n } catch (err) {\n // Ignore errors from stale requests\n if (currentRequestId !== requestIdRef.current) return;\n\n const errorMessage =\n err instanceof Error\n ? err.message\n : 'Não foi possível carregar os detalhes do estudante';\n setData(null);\n setError(errorMessage);\n } finally {\n // Only update loading state for current request\n if (currentRequestId === requestIdRef.current) {\n setLoading(false);\n }\n }\n },\n [api]\n );\n\n const reset = useCallback(() => {\n // Invalidate any in-flight requests\n requestIdRef.current++;\n setData(null);\n setLoading(false);\n setError(null);\n }, []);\n\n return { data, loading, error, fetchDetails, reset };\n}\n","import type { StudentDetailsParams } from './types';\n\n/**\n * Body type for POST request\n */\nexport interface StudentDetailsBody {\n userInstitutionId: string;\n period?: string;\n subjectId?: string | null;\n page?: number;\n limit?: number;\n}\n\n/**\n * Convert params to POST body format\n */\nexport function paramsToBody(params: StudentDetailsParams): StudentDetailsBody {\n return {\n userInstitutionId: params.userInstitutionId,\n period: params.period,\n subjectId: params.subjectId ?? undefined,\n page: params.page ?? 1,\n limit: params.limit ?? 20,\n };\n}\n","import type { ExcelCell, SheetConfig } from '../../utils/exportExcel';\nimport {\n isStudentSubjectsData,\n SIMULATED_PERFORMANCE_TAG_CONFIG,\n type StudentDetailsData,\n} from './types';\n\n/**\n * Montagem das abas do XLSX do `SimulatedStudentDetailsModal`. Funções puras:\n * nada de React, nada de requisição, nada de escrita em disco — quem grava o\n * arquivo é `downloadExcel`.\n *\n * REGRA QUE GOVERNA ESTE ARQUIVO: a planilha traz o que o modal DESENHA, nada\n * mais e nada menos. As abas espelham os blocos do componente:\n *\n * | Bloco na tela | Onde foi parar |\n * | ------------------------------------------------- | ---------------------------- |\n * | título (nome da matéria, só no nível 2) | 1ª linha do resumo |\n * | card do estudante: nome | resumo |\n * | card do estudante: \"escola - turma\" | resumo, duas linhas |\n * | card do estudante: média | resumo |\n * | card do estudante: badge de desempenho | resumo |\n * | lista de matérias (nível 1) | \"Desempenho por matéria\" |\n * | lista de habilidades (nível 2) | \"Desempenho por habilidade\" |\n *\n * NAVEGAÇÃO EM CASCATA: a segunda aba é a do nível VISÍVEL, uma só, nunca as\n * duas. Não é escolha estética — é a única que o princípio permite. O\n * `useSimulatedStudentDetails` guarda um `data` único e o TROCA a cada\n * navegação (`fetchDetails` faz `setData(null)` antes de gravar a resposta,\n * `useSimulatedStudentDetails.ts:83` e `:95`), então enquanto o nível 2 está na\n * tela a lista de matérias já não existe em memória, e vice-versa. Levar os\n * dois níveis exigiria disparar requisições que a tela não fez — uma por\n * matéria, no caso do nível 2, que só carrega a matéria clicada.\n *\n * O QUE FICA DE FORA, e por quê: mapear pelo tipo, e não pelo que a tela\n * desenha, poria na planilha número que o professor nunca viu. Conferido campo\n * a campo no componente:\n *\n * - `StudentDetailsInfo.studentId`, `.institutionId` e `.schoolYear` — o card\n * escreve `{school} - {class}`, e o ano escolar não aparece em lugar nenhum;\n * - `SubjectPerformanceItem.icon` — nunca renderizado;\n * - `SubjectPerformanceItem.color` — vira um ponto colorido, não texto;\n * - `SubjectPerformanceItem.performance.correct` e `.incorrect` — o nível 1\n * mostra só a barra de percentual e a contagem de questões; a contagem de\n * acertos aparece apenas no nível 2 (\"3 de 4\");\n * - `StudentContentPerformanceItem.performance.incorrect` — idem, nenhum nível\n * o escreve;\n * - `page`, `limit` e `total` — paginação de API, sem controle na tela;\n * - `StudentContentsData.subject` — o nome da matéria SAI na planilha, mas pela\n * `subjectName` que o componente passa, não por este campo: quem o título do\n * modal desenha é o `selectedSubject` do estado local\n * (`SimulatedStudentDetailsModal.tsx:100`). Os dois carregam o mesmo nome,\n * vindos da mesma API; a planilha usa o que o usuário lê.\n *\n * SEM ARQUIVO DE REGRA COMPARTILHADA, ao contrário dos dois modais irmãos\n * (`activityProgress.ts`, `lessonProgress.ts`): este modal não tem condição\n * própria por linha para as duas cópias divergirem. A única decisão que a\n * planilha repete da tela é qual nível está aberto, e o predicado dela —\n * `isStudentSubjectsData` — já mora em `types.ts` e já é o que o componente usa\n * (`SimulatedStudentDetailsModal.tsx:145`), então é ele que este arquivo\n * importa.\n */\n\n/**\n * Nome da aba de indicadores: 19 caracteres.\n *\n * Sem rótulo próprio na tela — o título do modal é `Desempenho de {nome}` no\n * nível 1 e o nome da matéria no nível 2, os dois variáveis e sem teto de\n * tamanho, o que daria um nome de aba instável. Dentro do limite de 31 do Excel\n * (acima disso o arquivo INTEIRO não abre) e sem nenhum dos caracteres que o\n * formato proíbe (`: \\ / ? * [ ]`).\n */\nexport const SUMMARY_SHEET_NAME = 'Resumo do estudante';\n\n/**\n * Nome da aba do nível 1: 25 caracteres.\n *\n * \"componente\" sozinho, e não \"componente curricular\" como no resto da tela:\n * o nome por extenso daria 36 caracteres e estouraria o limite de 31 do Excel,\n * acima do qual o arquivo INTEIRO não abre. O cabeçalho da coluna dentro da\n * aba (`SUBJECTS_HEADERS`) continua com o termo completo.\n */\nexport const SUBJECTS_SHEET_NAME = 'Desempenho por componente';\n\n/**\n * Nome da aba do nível 2: 25 caracteres.\n *\n * \"habilidade\" e não \"conteúdo\", que é como o TIPO chama\n * (`StudentContentPerformanceItem`): a tela escreve \"Nenhuma habilidade\n * encontrada\" quando a lista vem vazia, então é essa a palavra que o produto\n * usa para este nível.\n */\nexport const CONTENTS_SHEET_NAME = 'Desempenho por habilidade';\n\n/** Célula sem valor — o código BNCC que a tela omite quando vem nulo. */\nconst EMPTY_CELL = '';\n\nconst SUMMARY_HEADERS = ['Indicador', 'Valor'];\n\n/**\n * Rótulos das linhas do resumo.\n *\n * Textos que a planilha acrescenta: no card da tela nome, escola, turma, média\n * e badge aparecem sem rótulo nenhum, e uma tabela de chave/valor precisa de\n * chave. Escola e turma saem em duas linhas, embora o card as junte em\n * `{school} - {class}`, porque duas colunas filtram do outro lado e o traço não\n * carrega informação.\n */\nconst SUBJECT_ROW_LABEL = 'Componente curricular';\nconst STUDENT_ROW_LABEL = 'Estudante';\nconst SCHOOL_ROW_LABEL = 'Escola';\nconst CLASS_ROW_LABEL = 'Turma';\nconst AVERAGE_ROW_LABEL = 'Média';\nconst PERFORMANCE_ROW_LABEL = 'Desempenho';\n\n/**\n * Colunas do nível 1, na ordem em que o `SubjectItem` as desenha: nome,\n * contagem de questões, barra de percentual.\n */\nconst SUBJECTS_HEADERS = ['Componente curricular', 'Questões', 'Acertos (%)'];\n\n/**\n * Colunas do nível 2, na ordem em que o `ContentItem` as desenha: nome, código\n * BNCC, contagem de questões, barra de percentual e a contagem de acertos que\n * fecha a linha (\"3 de 4\" — o total já é a coluna \"Questões\").\n */\nconst CONTENTS_HEADERS = [\n 'Habilidade',\n 'Código BNCC',\n 'Questões',\n 'Acertos (%)',\n 'Acertos',\n];\n\n/**\n * Percentual como a tela o arredonda, em célula NUMÉRICA — `75` e não `\"75%\"`.\n *\n * O \"%\" é formatação de exibição, e um número é o que permite ordenar e somar\n * do outro lado; é a mesma diferença deliberada que os dois modais irmãos já\n * adotam. `Math.round` é o que os dois lugares da tela aplicam: a média do card\n * passa por `formatPercentageRounded` (`utils/utils.ts:32-34`) e o percentual\n * de cada linha pela `ProgressBar`, que escreve `Math.round(percentage)`\n * (`ProgressBar.tsx:576`).\n *\n * O que NÃO é copiado da `ProgressBar` são as defesas de entrada — ela também\n * troca `NaN` por 0 e limita o valor a 0..100 antes de arredondar\n * (`ProgressBar.tsx:164-170`) —, porque `correctPercentage` e `average` chegam\n * como percentual já calculado pelo backend; um valor fora da faixa sairia\n * diferente aqui e na tela, e é o número cru que a planilha deve mostrar. É a\n * mesma escolha que o `StudentLessonProgressModal` fez diante do\n * `ProgressCircle`.\n */\nconst roundPercentage = (value: number): number => Math.round(value);\n\n/**\n * Aba de indicadores: uma linha por informação visível do cabeçalho, na ordem\n * da tela.\n *\n * A linha da matéria só existe quando a tela mostra o nome dela — isto é, no\n * nível 2, onde ela é o TÍTULO do modal, acima do card. Por isso vem primeiro.\n * No nível 1 o título é `Desempenho de {nome}`, que não acrescenta nada à linha\n * \"Estudante\" logo abaixo.\n *\n * @param data - O que o modal tem carregado; `null` nos estados de carregando,\n * erro e sem-dado, em que a tela não desenha o card e a aba sai só com\n * cabeçalho.\n * @param subjectName - O nome que o título do modal está exibindo, ou `null` no\n * nível 1. Vem do componente, não de `data`, porque é o estado local\n * `selectedSubject` que a tela renderiza.\n */\nexport function buildSummarySheet(\n data: StudentDetailsData | null,\n subjectName: string | null\n): SheetConfig {\n if (!data) {\n return { name: SUMMARY_SHEET_NAME, headers: SUMMARY_HEADERS, rows: [] };\n }\n\n const { student } = data;\n const rows: ExcelCell[][] = [];\n\n if (subjectName) {\n rows.push([SUBJECT_ROW_LABEL, subjectName]);\n }\n\n rows.push(\n [STUDENT_ROW_LABEL, student.name],\n [SCHOOL_ROW_LABEL, student.school],\n [CLASS_ROW_LABEL, student.class],\n [AVERAGE_ROW_LABEL, roundPercentage(student.average)],\n [\n PERFORMANCE_ROW_LABEL,\n SIMULATED_PERFORMANCE_TAG_CONFIG[student.performance].label,\n ]\n );\n\n return { name: SUMMARY_SHEET_NAME, headers: SUMMARY_HEADERS, rows };\n}\n\n/**\n * Aba da lista: a do nível que está aberto, uma linha por item.\n *\n * Sem dado carregado cai no nível 1, que é onde o modal sempre abre — o efeito\n * de abertura busca com `subjectId: null`\n * (`SimulatedStudentDetailsModal.tsx:43-48`).\n *\n * A aba existe mesmo com a lista vazia, embora a tela troque a lista por\n * \"Nenhuma matéria encontrada\" / \"Nenhuma habilidade encontrada\" nesse caso:\n * uma aba só com cabeçalho diz \"não havia itens\", enquanto um conjunto de abas\n * que muda de tamanho conforme o dado quebra quem consome o arquivo. A mensagem\n * em si fica de fora — ela é a ausência de linhas, e escrevê-la como linha\n * inventaria um item que não existe.\n */\nexport function buildDetailsSheet(\n data: StudentDetailsData | null\n): SheetConfig {\n if (data && !isStudentSubjectsData(data)) {\n const rows: ExcelCell[][] = data.contents.map((content) => [\n content.contentName,\n content.bnccCode || EMPTY_CELL,\n content.questionsCount,\n roundPercentage(content.performance.correctPercentage),\n content.performance.correct,\n ]);\n\n return { name: CONTENTS_SHEET_NAME, headers: CONTENTS_HEADERS, rows };\n }\n\n const rows: ExcelCell[][] = (data?.subjects ?? []).map((subject) => [\n subject.name,\n subject.questionsCount,\n roundPercentage(subject.performance.correctPercentage),\n ]);\n\n return { name: SUBJECTS_SHEET_NAME, headers: SUBJECTS_HEADERS, rows };\n}\n\n/**\n * As abas da planilha deste modal, na ordem em que os blocos aparecem na tela.\n *\n * São sempre DUAS: o resumo do cabeçalho e a lista do nível visível. A segunda\n * troca de nome e de colunas conforme o nível, porque uma aba chamada\n * \"Desempenho por matéria\" com habilidades dentro mentiria; o que não muda é a\n * quantidade.\n */\nexport function buildSimulatedStudentDetailsSheets(\n data: StudentDetailsData | null,\n subjectName: string | null\n): SheetConfig[] {\n return [buildSummarySheet(data, subjectName), buildDetailsSheet(data)];\n}\n","import type { OverviewAggregationType } from '../components/SimulatedStudentsOverview/types';\nimport { PROFILE_ROLES } from '../types/chat';\n\n/**\n * Get the overview aggregation type based on user profile\n *\n * - GENERAL_MANAGER: students (default)\n * - REGIONAL_MANAGER: municipalities (aggregated by city)\n * - UNIT_MANAGER: classes (aggregated by turma)\n * - TEACHER: students (default)\n *\n * @param profileName - The user's profile name\n * @returns The aggregation type to use\n */\nexport function getAggregationTypeByProfile(\n profileName: string | undefined\n): OverviewAggregationType {\n switch (profileName) {\n case PROFILE_ROLES.UNIT_MANAGER:\n return 'classes';\n case PROFILE_ROLES.REGIONAL_MANAGER:\n return 'municipalities';\n case PROFILE_ROLES.GENERAL_MANAGER:\n case PROFILE_ROLES.TEACHER:\n default:\n return 'students';\n }\n}\n\n/**\n * Check if the profile should use aggregated overview (classes or municipalities)\n *\n * @param profileName - The user's profile name\n * @returns True if the profile should use aggregated overview\n */\nexport function shouldUseAggregatedOverview(\n profileName: string | undefined\n): boolean {\n return (\n profileName === PROFILE_ROLES.UNIT_MANAGER ||\n profileName === PROFILE_ROLES.REGIONAL_MANAGER\n );\n}\n","import type { ColumnConfig } from '../TableProvider';\nimport type { StudentsHighlightPeriod } from '../../hooks/useStudentsHighlight';\nimport type { SimulatedFilters } from '../SimulatedFilters/types';\nimport type { ScoreType } from '../../types/common';\nimport type { GeneralOverviewData } from '../GeneralOverviewSection/types';\nimport type {\n SimulatedOverviewData,\n SimulatedStudentItem,\n AggregatedOverviewData,\n OverviewAggregationType,\n} from '../SimulatedStudentsOverview/types';\nimport type {\n ContentsPerformanceData,\n SimulatedContentItem,\n} from '../SimulatedContentsPerformance/types';\nimport type { BaseApiClient } from '../../types/api';\n\n/**\n * View tabs for simulated exams (students vs skills)\n */\nexport enum SimulatedViewTab {\n STUDENTS = 'students',\n SKILLS = 'skills',\n}\n\nexport interface UseSimulatedPerformanceOptions {\n api: BaseApiClient;\n /**\n * User's profile name to determine aggregation type.\n * - UNIT_MANAGER: aggregates by classes\n * - REGIONAL_MANAGER: aggregates by municipalities\n * - GENERAL_MANAGER, TEACHER, others: shows students (default)\n */\n profileName?: string;\n /**\n * When false, the hook will not make automatic API calls (effect-triggered fetches).\n * The initial data load and effect-based reloads (e.g., scoreType URL changes) will be\n * skipped. However, manual handler calls (handlePeriodChange, handleFiltersApply, etc.)\n * will still trigger API requests if invoked directly.\n * Useful for lazy loading when the simulated report tab is not active.\n * @default true\n */\n enabled?: boolean;\n}\n\nexport interface UseSimulatedPerformanceReturn {\n // === Query Params (gerenciados internamente) ===\n period: StudentsHighlightPeriod;\n scoreType: ScoreType;\n\n // === Estados de Seleção ===\n selectedAreaKnowledgeId: string | null;\n /** Related IDs for merged areas (includes all IDs when area was deduplicated) */\n selectedAreaRelatedIds: string[];\n selectedSubjectId: string | null;\n simulatedViewTab: SimulatedViewTab;\n isEssaySelected: boolean;\n\n // === Filtros ===\n filters: SimulatedFilters;\n activeFiltersCount: number;\n\n // === Aggregation Type ===\n aggregationType: OverviewAggregationType;\n\n // === Dados da API ===\n generalOverview: {\n data: GeneralOverviewData | null;\n loading: boolean;\n error: string | null;\n };\n /**\n * Aggregated overview data based on profile.\n * - students: contains StudentsOnlyOverviewData\n * - classes: contains ClassesOverviewData\n * - municipalities: contains MunicipalitiesOverviewData\n */\n aggregatedOverview: {\n data: AggregatedOverviewData | null;\n loading: boolean;\n isRefreshing: boolean;\n error: string | null;\n };\n /**\n * Paginated students list for the table (legacy endpoint).\n * Still used for drilling into individual student details.\n */\n studentsOverview: {\n data: SimulatedOverviewData | null;\n loading: boolean;\n isRefreshing: boolean;\n error: string | null;\n };\n contentsPerformance: {\n data: ContentsPerformanceData | null;\n loading: boolean;\n isRefreshing: boolean;\n error: string | null;\n };\n\n // === Handlers ===\n handlePeriodChange: (period: string) => void;\n handleScoreTypeChange: (scoreType: string) => void;\n handleAreaKnowledgeChange: (areaId: string | null) => void;\n handleSubjectChange: (subjectId: string | null) => void;\n handleViewTabChange: (tab: string) => void;\n handleFiltersApply: (filters: SimulatedFilters) => void;\n handleStudentsParamsChange: (params: Record<string, unknown>) => void;\n handleContentsParamsChange: (params: Record<string, unknown>) => void;\n handleStudentRowClick: (row: SimulatedStudentItem) => void;\n handleContentRowClick: (row: SimulatedContentItem) => void;\n\n // === Modais ===\n studentModal: {\n isOpen: boolean;\n student: { userInstitutionId: string; name: string } | null;\n close: () => void;\n };\n contentModal: {\n isOpen: boolean;\n content: { contentId: string; contentName: string } | null;\n close: () => void;\n };\n filtersModal: {\n isOpen: boolean;\n open: () => void;\n close: () => void;\n };\n\n // === Colunas das Tabelas ===\n studentsTableColumns: ColumnConfig<SimulatedStudentItem>[];\n contentsTableColumns: ColumnConfig<SimulatedContentItem>[];\n}\n\nexport interface SimulatedPerformanceViewProps extends UseSimulatedPerformanceReturn {\n api: BaseApiClient;\n noSearchImage?: string;\n}\n"]}
|