UNPKG

analytica-frontend-lib

Version:

Repositório público dos componentes utilizados nas plataformas da Analytica Ensino

1,445 lines (1,424 loc) 62.6 kB
import { SelectContent, SelectItem, SelectTrigger, SelectValue, Select_default } from "./chunk-UU7Q5S52.mjs"; import { SkeletonRounded } from "./chunk-NKVUUWCA.mjs"; import { ProgressBar_default } from "./chunk-LDBWF6EA.mjs"; import { IconRender_default } from "./chunk-QEZJZHJG.mjs"; import { Badge_default } from "./chunk-OEW3ST4F.mjs"; import { Modal_default } from "./chunk-TNLGS7SB.mjs"; import { Button_default } from "./chunk-LAYB7IKW.mjs"; import { Text_default } from "./chunk-IMCIR6TJ.mjs"; import { cn, formatPercentageRounded, formatScore, hexToRgba } from "./chunk-53ICLDGS.mjs"; // src/components/SimulatedPerformance/useSimulatedPerformance.tsx import { useEffect as useEffect2, useCallback as useCallback7, useState as useState7, useMemo as useMemo2, useRef as useRef6 } from "react"; import { useSearchParams } from "react-router-dom"; // src/components/GeneralOverviewSection/GeneralOverviewSection.tsx import { jsx, jsxs } from "react/jsx-runtime"; function AreaCard({ area, scoreType = "percentage" /* PERCENTAGE */ }) { const bgColor = hexToRgba(area.color, 0.1); return /* @__PURE__ */ jsxs( "div", { className: "flex-1 min-w-0 flex flex-col items-center justify-center p-4 rounded-xl", style: { backgroundColor: bgColor }, children: [ /* @__PURE__ */ jsx( "div", { className: "w-8 h-8 flex items-center justify-center rounded-full mb-2", style: { backgroundColor: area.color }, children: /* @__PURE__ */ jsx(IconRender_default, { iconName: area.icon, size: 18, color: "white" }) } ), /* @__PURE__ */ jsx( Text_default, { size: "xs", weight: "semibold", className: "text-center uppercase text-text-700 mb-1 line-clamp-2", children: area.name } ), /* @__PURE__ */ jsx(Text_default, { size: "lg", weight: "bold", className: "text-text-950", children: formatScore(area.percentage, scoreType) }) ] } ); } function EssayCard({ essay }) { const bgColor = hexToRgba(essay.color, 0.1); return /* @__PURE__ */ jsxs( "div", { className: "flex-1 min-w-0 flex flex-col items-center justify-center p-4 rounded-xl", style: { backgroundColor: bgColor }, children: [ /* @__PURE__ */ jsx( "div", { className: "w-8 h-8 flex items-center justify-center rounded-full mb-2", style: { backgroundColor: essay.color }, children: /* @__PURE__ */ jsx(IconRender_default, { iconName: essay.icon, size: 18, color: "white" }) } ), /* @__PURE__ */ jsx( Text_default, { size: "xs", weight: "semibold", className: "text-center uppercase text-text-700 mb-1 line-clamp-2", children: essay.name } ), /* @__PURE__ */ jsx(Text_default, { size: "lg", weight: "bold", className: "text-text-950", children: formatScore(essay.percentage, "percentage" /* PERCENTAGE */) }) ] } ); } function AreaCardSkeleton() { return /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 flex flex-col items-center justify-center p-4 rounded-xl bg-background-50", children: [ /* @__PURE__ */ jsx(SkeletonRounded, { className: "h-8 w-8 rounded-full mb-2" }), /* @__PURE__ */ jsx(SkeletonRounded, { className: "h-3 w-16 mb-1" }), /* @__PURE__ */ jsx(SkeletonRounded, { className: "h-6 w-12" }) ] }); } function GeneralOverviewSection({ data, loading = false, error = null, scoreType = "percentage" /* PERCENTAGE */ }) { if (error) { return /* @__PURE__ */ jsx("div", { className: "p-4 bg-error-50 border border-error-200 rounded-xl", children: /* @__PURE__ */ jsx(Text_default, { size: "sm", className: "text-error-700", children: error }) }); } if (loading) { return /* @__PURE__ */ jsxs("div", { className: "space-y-4 p-5 bg-background border border-border-50 rounded-xl", children: [ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [ /* @__PURE__ */ jsx(Text_default, { size: "lg", weight: "bold", className: "text-text-950", children: "Geral" }), /* @__PURE__ */ jsx(SkeletonRounded, { className: "h-4 w-3/4" }) ] }), /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [ /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(SkeletonRounded, { className: "h-3 w-full rounded-full" }) }), /* @__PURE__ */ jsx(SkeletonRounded, { className: "h-6 w-12" }) ] }), /* @__PURE__ */ jsx("div", { className: "flex gap-3", children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsx(AreaCardSkeleton, {}, i)) }) ] }); } if (!data) { return null; } return /* @__PURE__ */ jsxs("div", { className: "space-y-4 p-5 bg-background border border-border-50 rounded-xl", children: [ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [ /* @__PURE__ */ jsx(Text_default, { size: "lg", weight: "bold", className: "text-text-950", children: "Geral" }), /* @__PURE__ */ jsx(Text_default, { size: "sm", className: "text-text-500", children: "Dados que mostram a profici\xEAncia de todos os simulados digitais j\xE1 realizados em todas as \xE1reas do conhecimento" }) ] }), /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [ /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx( ProgressBar_default, { value: scoreType === "tri" /* TRI */ ? data.overallPercentage / 10 : data.overallPercentage, variant: "green", size: "small" } ) }), /* @__PURE__ */ jsx( Text_default, { size: "lg", weight: "bold", className: "text-text-950 min-w-[50px] text-right", children: formatScore(data.overallPercentage, scoreType) } ) ] }), (data.areas.length > 0 || data.essay) && /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [ data.areas.map((area) => /* @__PURE__ */ jsx(AreaCard, { area, scoreType }, area.id)), data.essay && /* @__PURE__ */ jsx(EssayCard, { essay: data.essay }) ] }) ] }); } // src/components/GeneralOverviewSection/useGeneralOverview.ts import { useState, useCallback, useRef } from "react"; var initialState = { data: null, loading: false, isRefreshing: false, error: null }; function useGeneralOverview(api) { const [state, setState] = useState(initialState); const requestIdRef = useRef(0); const fetchOverview = useCallback( async (params, refresh = false) => { const currentRequestId = ++requestIdRef.current; setState((prev) => ({ ...prev, loading: !refresh, isRefreshing: refresh, error: null })); try { let url = "/performance/simulated/general-overview"; if (params.scoreType && params.scoreType !== "percentage") { url += `?scoreType=${params.scoreType}`; } const body = { period: params.period, schoolIds: params.schoolIds || [], schoolYearIds: params.schoolYearIds || [], classIds: params.classIds || [] }; const response = await api.post(url, body); if (currentRequestId !== requestIdRef.current) return; setState({ data: response.data.data, loading: false, isRefreshing: false, error: null }); } catch (err) { if (currentRequestId !== requestIdRef.current) return; const errorMessage = err instanceof Error ? err.message : "Erro ao carregar dados gerais de desempenho"; console.error("Error fetching general overview:", err); setState((prev) => ({ ...prev, loading: false, isRefreshing: false, error: errorMessage })); } }, [api] ); const reset = useCallback(() => { requestIdRef.current++; setState(initialState); }, []); return { ...state, fetchOverview, reset }; } // src/components/SimulatedStudentsOverview/useSimulatedOverview.ts import { useState as useState2, useCallback as useCallback2, useRef as useRef2 } from "react"; // src/components/SimulatedStudentDetailsModal/types.ts var SIMULATED_PERFORMANCE_TAG_CONFIG = { ["HIGHLIGHT" /* HIGHLIGHT */]: { label: "Destaque da turma", variant: "success" /* SUCCESS */ }, ["ABOVE_AVERAGE" /* ABOVE_AVERAGE */]: { label: "Acima da m\xE9dia", variant: "info" /* INFO */ }, ["BELOW_AVERAGE" /* BELOW_AVERAGE */]: { label: "Abaixo da m\xE9dia", variant: "warning" /* WARNING */ }, ["ATTENTION_POINT" /* ATTENTION_POINT */]: { label: "Ponto de aten\xE7\xE3o", variant: "error" /* ERROR */ } }; var PerformanceBadgeAction = /* @__PURE__ */ ((PerformanceBadgeAction2) => { PerformanceBadgeAction2["SUCCESS"] = "success"; PerformanceBadgeAction2["INFO"] = "info"; PerformanceBadgeAction2["WARNING"] = "warning"; PerformanceBadgeAction2["ERROR"] = "error"; return PerformanceBadgeAction2; })(PerformanceBadgeAction || {}); var PERFORMANCE_TAG_TO_BADGE_ACTION = { ["HIGHLIGHT" /* HIGHLIGHT */]: "success" /* SUCCESS */, ["ABOVE_AVERAGE" /* ABOVE_AVERAGE */]: "info" /* INFO */, ["BELOW_AVERAGE" /* BELOW_AVERAGE */]: "warning" /* WARNING */, ["ATTENTION_POINT" /* ATTENTION_POINT */]: "error" /* ERROR */ }; var ReportSimulationType = /* @__PURE__ */ ((ReportSimulationType2) => { ReportSimulationType2["ENEM_1"] = "enem-1"; ReportSimulationType2["ENEM_2"] = "enem-2"; ReportSimulationType2["ESSAYS"] = "essays"; return ReportSimulationType2; })(ReportSimulationType || {}); function isStudentSubjectsData(data) { return "subjects" in data; } function isStudentContentsData(data) { return "contents" in data; } function simulationTypeToActivityFilters(simulationType) { switch (simulationType) { case "enem-1" /* ENEM_1 */: return { types: ["SIMULADO"], subtypes: ["ENEM_PROVA_1"] }; case "enem-2" /* ENEM_2 */: return { types: ["SIMULADO"], subtypes: ["ENEM_PROVA_2"] }; default: return {}; } } // src/components/SimulatedStudentsOverview/utils.ts function getErrorMessage(err, fallbackMessage) { return err instanceof Error ? err.message : fallbackMessage; } function isStudentsData(data, type) { return type === "students" && data !== null && !("totalClasses" in data) && !("totalMunicipalities" in data); } function isClassesData(data, type) { return type === "classes" && data !== null && "totalClasses" in data; } function isMunicipalitiesData(data, type) { return type === "municipalities" && data !== null && "totalMunicipalities" in data; } // src/components/SimulatedStudentsOverview/useSimulatedOverview.ts function buildEndpoint(simulationType, scoreType) { if (simulationType === "essays") { const params2 = new URLSearchParams(); if (scoreType && scoreType !== "percentage") { params2.append("scoreType", scoreType); } const queryString2 = params2.toString(); const endpoint2 = "/performance/simulated/essays/students-overview"; if (!queryString2) { return endpoint2; } return `${endpoint2}?${queryString2}`; } const activityFilters = simulationTypeToActivityFilters(simulationType); const params = new URLSearchParams(); activityFilters.types?.forEach((t) => params.append("types", t)); activityFilters.statuses?.forEach((s) => params.append("statuses", s)); if (scoreType && scoreType !== "percentage") { params.append("scoreType", scoreType); } const queryString = params.toString(); const endpoint = "/performance/simulated/activities/students-overview"; if (!queryString) { return endpoint; } return `${endpoint}?${queryString}`; } var initialState2 = { data: null, loading: false, isRefreshing: false, error: null }; function useSimulatedOverview(api) { const [data, setData] = useState2( initialState2.data ); const [loading, setLoading] = useState2(initialState2.loading); const [isRefreshing, setIsRefreshing] = useState2(initialState2.isRefreshing); const [error, setError] = useState2(initialState2.error); const requestIdRef = useRef2(0); const paramsToBody2 = (params) => { return { period: params.period, subjectId: params.subjectId, areaKnowledgeId: params.areaKnowledgeId, schoolIds: params.schoolIds, schoolYearIds: params.schoolYearIds, classIds: params.classIds, studentsIds: params.studentsIds, page: params.page ?? 1, limit: params.limit ?? 10, orderBy: params.orderBy ?? "name", order: params.order ?? "asc" }; }; const fetchOverview = useCallback2( async (params, refresh = false) => { const currentRequestId = ++requestIdRef.current; try { if (refresh) { setIsRefreshing(true); } else { setLoading(true); } setError(null); const endpoint = buildEndpoint(params.simulationType, params.scoreType); const body = paramsToBody2(params); const response = await api.post( endpoint, body ); if (currentRequestId !== requestIdRef.current) return; setData(response.data.data); } catch (err) { if (currentRequestId !== requestIdRef.current) return; setError( getErrorMessage( err, "N\xE3o foi poss\xEDvel carregar os dados de simulados" ) ); } finally { if (currentRequestId === requestIdRef.current) { setLoading(false); setIsRefreshing(false); } } }, [api] ); const reset = useCallback2(() => { requestIdRef.current++; setData(initialState2.data); setLoading(initialState2.loading); setIsRefreshing(initialState2.isRefreshing); setError(initialState2.error); }, []); return { data, loading, isRefreshing, error, fetchOverview, reset }; } // src/components/SimulatedStudentsOverview/useAggregatedOverview.ts import { useState as useState3, useCallback as useCallback3, useRef as useRef3 } from "react"; function buildEndpoint2(aggregationType, simulationType, scoreType) { if (simulationType === "essays") { return "/performance/simulated/essays/students-overview"; } const activityFilters = simulationTypeToActivityFilters(simulationType); const params = new URLSearchParams(); params.append("aggregationType", aggregationType); activityFilters.types?.forEach((t) => params.append("types", t)); activityFilters.statuses?.forEach((s) => params.append("statuses", s)); if (scoreType && scoreType !== "percentage") { params.append("scoreType", scoreType); } const basePath = "/performance/simulated/activities/overview"; return `${basePath}?${params.toString()}`; } var initialState3 = { data: null, loading: false, isRefreshing: false, error: null }; function useAggregatedOverview(api) { const [data, setData] = useState3( initialState3.data ); const [loading, setLoading] = useState3(initialState3.loading); const [isRefreshing, setIsRefreshing] = useState3(initialState3.isRefreshing); const [error, setError] = useState3(initialState3.error); const requestIdRef = useRef3(0); const paramsToBody2 = (params) => { return { period: params.period, subjectId: params.subjectId, areaKnowledgeId: params.areaKnowledgeId, schoolIds: params.schoolIds, schoolYearIds: params.schoolYearIds, classIds: params.classIds, studentsIds: params.studentsIds }; }; const fetchOverview = useCallback3( async (params, refresh = false) => { const currentRequestId = ++requestIdRef.current; try { if (refresh) { setIsRefreshing(true); } else { setLoading(true); } setError(null); const endpoint = buildEndpoint2( params.aggregationType, params.simulationType, params.scoreType ); const body = paramsToBody2(params); let responseData; switch (params.aggregationType) { case "classes": { const response = await api.post( endpoint, body ); responseData = response.data.data; break; } case "municipalities": { const response = await api.post( endpoint, body ); responseData = response.data.data; break; } case "students": default: { const response = await api.post( endpoint, body ); responseData = response.data.data; break; } } if (currentRequestId !== requestIdRef.current) return; setData(responseData); } catch (err) { if (currentRequestId !== requestIdRef.current) return; setError( getErrorMessage(err, "N\xE3o foi poss\xEDvel carregar os dados de overview") ); } finally { if (currentRequestId === requestIdRef.current) { setLoading(false); setIsRefreshing(false); } } }, [api] ); const reset = useCallback3(() => { requestIdRef.current++; setData(initialState3.data); setLoading(initialState3.loading); setIsRefreshing(initialState3.isRefreshing); setError(initialState3.error); }, []); return { data, loading, isRefreshing, error, fetchOverview, reset }; } // src/components/SimulatedContentsPerformance/useSimulatedContents.ts import { useState as useState4, useCallback as useCallback4, useRef as useRef4 } from "react"; function buildEndpoint3(simulationType, scoreType) { if (simulationType === "essays" /* ESSAYS */) { return null; } const activityFilters = simulationTypeToActivityFilters(simulationType); const params = new URLSearchParams(); activityFilters.types?.forEach((t) => params.append("types", t)); activityFilters.statuses?.forEach((s) => params.append("statuses", s)); if (scoreType && scoreType !== "percentage") { params.append("scoreType", scoreType); } const queryString = params.toString(); const endpoint = "/performance/simulated/activities/contents-performance"; if (!queryString) { return endpoint; } return `${endpoint}?${queryString}`; } function useSimulatedContents(api) { const [data, setData] = useState4(null); const [loading, setLoading] = useState4(false); const [isRefreshing, setIsRefreshing] = useState4(false); const [error, setError] = useState4(null); const isFirstLoad = useRef4(true); const latestRequestId = useRef4(0); const isCurrentRequest = (requestId) => requestId === latestRequestId.current; const buildRequestBody = (params) => ({ period: params.period, subjectId: params.subjectId && params.subjectId !== "all" ? params.subjectId : void 0, areaKnowledgeId: params.areaKnowledgeId && params.areaKnowledgeId !== "all" ? params.areaKnowledgeId : void 0, schoolIds: params.schoolIds, schoolYearIds: params.schoolYearIds, classIds: params.classIds, studentsIds: params.studentsIds, page: params.page ?? 1, limit: params.limit ?? 10, orderBy: params.orderBy ?? "correctPercentage", order: params.order ?? "asc" }); const handleNoEndpoint = (requestId) => { if (!isCurrentRequest(requestId)) { return; } setData(null); setError(null); setLoading(false); setIsRefreshing(false); isFirstLoad.current = true; }; const startRequestState = (requestId, refresh) => { if (!isCurrentRequest(requestId)) { return; } if (isFirstLoad.current && !refresh) { setLoading(true); } else { setIsRefreshing(true); } setError(null); }; const handleSuccess = (requestId, response) => { if (!isCurrentRequest(requestId)) { return; } setData(response.data.data); isFirstLoad.current = false; }; const handleError = (requestId, err) => { if (!isCurrentRequest(requestId)) { return; } const errorMessage = err instanceof Error ? err.message : "Erro ao carregar dados de habilidades"; setError(errorMessage); setData(null); }; const finishRequestState = (requestId) => { if (!isCurrentRequest(requestId)) { return; } setLoading(false); setIsRefreshing(false); }; const fetchContents = useCallback4( async (params, refresh = false) => { const localRequestId = ++latestRequestId.current; const endpoint = buildEndpoint3(params.simulationType, params.scoreType); if (!endpoint) { handleNoEndpoint(localRequestId); return; } startRequestState(localRequestId, refresh); try { const response = await api.post( endpoint, buildRequestBody(params) ); handleSuccess(localRequestId, response); } catch (err) { handleError(localRequestId, err); } finally { finishRequestState(localRequestId); } }, [api] ); const reset = useCallback4(() => { setData(null); setLoading(false); setIsRefreshing(false); setError(null); isFirstLoad.current = true; }, []); return { data, loading, isRefreshing, error, fetchContents, reset }; } // src/components/AreaKnowledgeSelector/AreaKnowledgeSelector.tsx import { useMemo } from "react"; import { GridFourIcon } from "@phosphor-icons/react/dist/csr/GridFour"; // src/components/AreaKnowledgeSelector/types.ts var ESSAY_AREA_ID = "essay"; // src/components/AreaKnowledgeSelector/AreaKnowledgeSelector.tsx import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime"; function AreaKnowledgeSelector({ areas, selectedAreaId, onAreaChange, loading = false, label = "\xC1rea de conhecimento", includeEssay = true }) { const selectItems = useMemo(() => { const allOption = { id: "all", name: "Todos" }; const areaItems = areas.map( (area) => ({ id: area.id, name: area.name, color: area.color }) ); const items = [allOption, ...areaItems]; if (includeEssay) { const essayOption = { id: ESSAY_AREA_ID, name: "Reda\xE7\xE3o" }; items.push(essayOption); } return items; }, [areas, includeEssay]); const handleValueChange = (value) => { if (value === "all") { onAreaChange(null); } else { onAreaChange(value); } }; const effectiveValue = selectedAreaId || "all"; return /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [ /* @__PURE__ */ jsx2(Text_default, { size: "sm", weight: "medium", className: "text-text-700", children: label }), /* @__PURE__ */ jsx2("div", { className: "relative", children: /* @__PURE__ */ jsxs2( Select_default, { defaultValue: "all", value: effectiveValue, onValueChange: handleValueChange, children: [ /* @__PURE__ */ jsx2( SelectTrigger, { className: cn("w-full", loading && "opacity-50"), disabled: loading, children: /* @__PURE__ */ jsx2(SelectValue, { placeholder: "Selecione uma \xE1rea" }) } ), /* @__PURE__ */ jsx2(SelectContent, { children: selectItems.map((item) => { let itemIcon = null; if (item.id === "all") { itemIcon = /* @__PURE__ */ jsx2("span", { className: "w-[21px] h-[21px] flex items-center justify-center", children: /* @__PURE__ */ jsx2( GridFourIcon, { size: 17, weight: "bold", className: "text-gray-600" } ) }); } else if (item.color) { itemIcon = /* @__PURE__ */ jsx2( "span", { className: "w-[21px] h-[21px] flex items-center justify-center rounded-sm", style: { backgroundColor: `${item.color}20` }, children: /* @__PURE__ */ jsx2( "span", { className: "w-2 h-2 rounded-full", style: { backgroundColor: item.color } } ) } ); } return /* @__PURE__ */ jsx2(SelectItem, { value: item.id, disabled: loading, children: /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [ itemIcon, /* @__PURE__ */ jsx2("span", { className: "whitespace-nowrap", children: item.name }) ] }) }, item.id); }) }) ] } ) }) ] }); } // src/components/SimulatedStudentDetailsModal/SimulatedStudentDetailsModal.tsx import { useEffect, useState as useState6, useCallback as useCallback6 } from "react"; import { ArrowLeftIcon } from "@phosphor-icons/react/dist/csr/ArrowLeft"; import { CaretRightIcon } from "@phosphor-icons/react/dist/csr/CaretRight"; // src/components/SimulatedStudentDetailsModal/useSimulatedStudentDetails.ts import { useState as useState5, useCallback as useCallback5, useRef as useRef5 } from "react"; // src/components/SimulatedStudentDetailsModal/utils.ts function paramsToBody(params) { return { userInstitutionId: params.userInstitutionId, period: params.period, subjectId: params.subjectId ?? void 0, page: params.page ?? 1, limit: params.limit ?? 20 }; } // src/components/SimulatedStudentDetailsModal/useSimulatedStudentDetails.ts function buildEndpoint4(simulationType) { if (simulationType === "essays" /* ESSAYS */) { return "/performance/simulated/essays/student-details"; } const activityFilters = simulationTypeToActivityFilters(simulationType); const params = new URLSearchParams(); activityFilters.types?.forEach((t) => params.append("types", t)); activityFilters.statuses?.forEach((s) => params.append("statuses", s)); const queryString = params.toString(); const endpoint = "/performance/simulated/activities/student-details"; if (!queryString) { return endpoint; } return `${endpoint}?${queryString}`; } function useSimulatedStudentDetails(api) { const [data, setData] = useState5(null); const [loading, setLoading] = useState5(false); const [error, setError] = useState5(null); const requestIdRef = useRef5(0); const fetchDetails = useCallback5( async (params) => { const currentRequestId = ++requestIdRef.current; try { setLoading(true); setError(null); setData(null); const endpoint = buildEndpoint4(params.simulationType); const body = paramsToBody(params); const response = await api.post( endpoint, body ); if (currentRequestId !== requestIdRef.current) return; setData(response.data.data); } catch (err) { if (currentRequestId !== requestIdRef.current) return; const errorMessage = err instanceof Error ? err.message : "N\xE3o foi poss\xEDvel carregar os detalhes do estudante"; setData(null); setError(errorMessage); } finally { if (currentRequestId === requestIdRef.current) { setLoading(false); } } }, [api] ); const reset = useCallback5(() => { requestIdRef.current++; setData(null); setLoading(false); setError(null); }, []); return { data, loading, error, fetchDetails, reset }; } // src/components/SimulatedStudentDetailsModal/SimulatedStudentDetailsModal.tsx import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime"; function SimulatedStudentDetailsModal({ api, isOpen, onClose, simulationType, userInstitutionId, studentName, period }) { const { data, loading, error, fetchDetails, reset } = useSimulatedStudentDetails(api); const [selectedSubject, setSelectedSubject] = useState6(null); useEffect(() => { if (isOpen && userInstitutionId) { fetchDetails({ simulationType, userInstitutionId, period, subjectId: null }); } }, [isOpen, userInstitutionId, simulationType, period, fetchDetails]); useEffect(() => { if (!isOpen) { reset(); setSelectedSubject(null); } }, [isOpen, reset]); const handleSubjectClick = useCallback6( (subject) => { if (!userInstitutionId) return; setSelectedSubject({ id: subject.id, name: subject.name }); fetchDetails({ simulationType, userInstitutionId, period, subjectId: subject.id }); }, [userInstitutionId, simulationType, period, fetchDetails] ); const handleBack = useCallback6(() => { if (!userInstitutionId) return; setSelectedSubject(null); fetchDetails({ simulationType, userInstitutionId, period, subjectId: null }); }, [userInstitutionId, simulationType, period, fetchDetails]); const modalTitle = selectedSubject ? /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ jsx3( Button_default, { onClick: handleBack, variant: "raw", className: "p-1 hover:bg-background-100 rounded-md transition-colors", "aria-label": "Voltar para lista de mat\xE9rias", children: /* @__PURE__ */ jsx3(ArrowLeftIcon, { size: 20, className: "text-text-600" }) } ), /* @__PURE__ */ jsx3(Text_default, { children: selectedSubject.name }) ] }) : `Desempenho de ${studentName || "Estudante"}`; if (loading) { return /* @__PURE__ */ jsx3(Modal_default, { isOpen, onClose, title: modalTitle, size: "lg", children: /* @__PURE__ */ jsx3("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx3(Text_default, { size: "sm", className: "text-text-500", children: "Carregando..." }) }) }); } if (error) { return /* @__PURE__ */ jsx3(Modal_default, { isOpen, onClose, title: modalTitle, size: "lg", children: /* @__PURE__ */ jsx3("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx3(Text_default, { size: "sm", className: "text-error-500", children: error }) }) }); } if (!data) { return /* @__PURE__ */ jsx3(Modal_default, { isOpen, onClose, title: modalTitle, size: "lg", children: /* @__PURE__ */ jsx3("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx3(Text_default, { size: "sm", className: "text-text-500", children: "Nenhum dado encontrado" }) }) }); } const isSubjectsLevel = isStudentSubjectsData(data); const renderDetailsContent = () => { if (isSubjectsLevel) { if (data.subjects.length > 0) { return data.subjects.map((subject) => /* @__PURE__ */ jsx3( SubjectItem, { subject, onClick: () => handleSubjectClick(subject), questionsLabel: "quest\xF5es" }, subject.id )); } return /* @__PURE__ */ jsx3(Text_default, { size: "sm", className: "text-text-500 text-center py-4", children: "Nenhuma mat\xE9ria encontrada" }); } if (data.contents.length > 0) { return data.contents.map((content) => /* @__PURE__ */ jsx3( ContentItem, { content, questionsLabel: "quest\xF5es", ofLabel: "de" }, content.contentId )); } return /* @__PURE__ */ jsx3(Text_default, { size: "sm", className: "text-text-500 text-center py-4", children: "Nenhuma habilidade encontrada" }); }; const detailsContent = renderDetailsContent(); return /* @__PURE__ */ jsx3(Modal_default, { isOpen, onClose, title: modalTitle, size: "lg", children: /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-4", children: [ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between p-4 bg-background-50 rounded-xl", children: [ /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-1", children: [ /* @__PURE__ */ jsx3(Text_default, { size: "md", weight: "semibold", className: "text-text-950", children: data.student.name }), /* @__PURE__ */ jsxs3(Text_default, { size: "sm", className: "text-text-500", children: [ data.student.school, " - ", data.student.class ] }) ] }), /* @__PURE__ */ jsxs3("div", { className: "flex flex-col items-end gap-1", children: [ /* @__PURE__ */ jsx3(Text_default, { size: "lg", weight: "bold", className: "text-text-950", children: formatPercentageRounded(data.student.average) }), /* @__PURE__ */ jsx3( Badge_default, { variant: "solid", action: PERFORMANCE_TAG_TO_BADGE_ACTION[data.student.performance], size: "small", children: SIMULATED_PERFORMANCE_TAG_CONFIG[data.student.performance].label } ) ] }) ] }), /* @__PURE__ */ jsx3("div", { className: "flex flex-col gap-2 max-h-[400px] overflow-y-auto", children: detailsContent }) ] }) }); } function SubjectItem({ subject, onClick, questionsLabel }) { return /* @__PURE__ */ jsxs3( "button", { onClick, 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", children: [ subject.color && /* @__PURE__ */ jsx3( "div", { className: "w-3 h-3 rounded-full flex-shrink-0", style: { backgroundColor: subject.color } } ), /* @__PURE__ */ jsxs3("div", { className: "flex-1 min-w-0", children: [ /* @__PURE__ */ jsx3(Text_default, { size: "sm", weight: "semibold", className: "text-text-950 truncate", children: subject.name }), /* @__PURE__ */ jsxs3(Text_default, { size: "xs", className: "text-text-500", children: [ subject.questionsCount, " ", questionsLabel ] }) ] }), /* @__PURE__ */ jsx3("div", { className: "w-32 flex-shrink-0", children: /* @__PURE__ */ jsx3( ProgressBar_default, { value: subject.performance.correctPercentage, variant: "green", size: "small", showPercentage: true } ) }), /* @__PURE__ */ jsx3(CaretRightIcon, { size: 16, className: "text-text-400 flex-shrink-0" }) ] } ); } function ContentItem({ content, questionsLabel, ofLabel }) { return /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-4 p-4 bg-background border border-border-50 rounded-xl", children: [ /* @__PURE__ */ jsxs3("div", { className: "flex-1 min-w-0", children: [ /* @__PURE__ */ jsx3(Text_default, { size: "sm", weight: "semibold", className: "text-text-950 truncate", children: content.contentName }), /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [ content.bnccCode && /* @__PURE__ */ jsx3(Text_default, { size: "xs", className: "text-primary-600", children: content.bnccCode }), /* @__PURE__ */ jsxs3(Text_default, { size: "xs", className: "text-text-500", children: [ content.questionsCount, " ", questionsLabel ] }) ] }) ] }), /* @__PURE__ */ jsx3("div", { className: "w-32 flex-shrink-0", children: /* @__PURE__ */ jsx3( ProgressBar_default, { value: content.performance.correctPercentage, variant: "green", size: "small", showPercentage: true } ) }), /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1 flex-shrink-0", children: [ /* @__PURE__ */ jsx3(Text_default, { size: "sm", weight: "semibold", className: "text-success-600", children: content.performance.correct }), /* @__PURE__ */ jsxs3(Text_default, { size: "xs", className: "text-text-500 whitespace-nowrap", children: [ ofLabel, " ", content.questionsCount ] }) ] }) ] }); } // src/utils/profileAggregation.ts function getAggregationTypeByProfile(profileName) { switch (profileName) { case "UNIT_MANAGER" /* UNIT_MANAGER */: return "classes"; case "REGIONAL_MANAGER" /* REGIONAL_MANAGER */: return "municipalities"; case "GENERAL_MANAGER" /* GENERAL_MANAGER */: case "TEACHER" /* TEACHER */: default: return "students"; } } function shouldUseAggregatedOverview(profileName) { return profileName === "UNIT_MANAGER" /* UNIT_MANAGER */ || profileName === "REGIONAL_MANAGER" /* REGIONAL_MANAGER */; } // src/components/SimulatedPerformance/types.ts var SimulatedViewTab = /* @__PURE__ */ ((SimulatedViewTab2) => { SimulatedViewTab2["STUDENTS"] = "students"; SimulatedViewTab2["SKILLS"] = "skills"; return SimulatedViewTab2; })(SimulatedViewTab || {}); // src/components/SimulatedPerformance/useSimulatedPerformance.tsx import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime"; function SimulatedPerformanceTagCell({ row }) { const tagConfig = SIMULATED_PERFORMANCE_TAG_CONFIG[row.performance]; return /* @__PURE__ */ jsx4(Badge_default, { variant: "solid", action: tagConfig.variant, size: "small", children: tagConfig.label }); } function ContentPerformanceCell({ percentage }) { const getColorClass = () => { if (percentage >= 70) { return "bg-success-500"; } if (percentage >= 50) { return "bg-warning-500"; } return "bg-error-500"; }; const colorClass = getColorClass(); return /* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-center gap-2", children: [ /* @__PURE__ */ jsxs4("span", { className: "text-sm text-text-950 min-w-[40px]", children: [ Math.round(percentage), "%" ] }), /* @__PURE__ */ jsx4("div", { className: "w-16 h-2 bg-background-100 rounded-full overflow-hidden", children: /* @__PURE__ */ jsx4( "div", { className: `h-full rounded-full ${colorClass}`, style: { width: `${percentage}%` } } ) }) ] }); } var centeredColumn = { sortable: true, className: "py-3 px-4 text-center", align: "center" }; var centeredContentsColumn = { sortable: true, className: "py-3 px-4 text-center", align: "center" }; var simulatedTableColumnsBase = [ { key: "name", label: "Nome", sortable: true, className: "py-3 px-4 text-start" }, { key: "school", label: "Escola", sortable: false, className: "py-3 px-4 text-start" }, { ...centeredColumn, key: "schoolYear", label: "Ano", width: "80px", sortable: false }, { ...centeredColumn, key: "class", label: "Turma", width: "80px", sortable: false }, { ...centeredColumn, key: "average", label: "M\xE9dia", width: "100px" }, { ...centeredColumn, key: "performance", label: "Desempenho", width: "160px" } ]; var contentsTableColumns = [ { key: "contentName", label: "Habilidade", sortable: true, className: "py-3 px-4 text-start" }, { key: "subject", label: "Componente", sortable: false, className: "py-3 px-4 text-start", render: (_value, row) => row.subject.name }, { ...centeredContentsColumn, key: "simulatedExamsCount", label: "Simulados", width: "100px" }, { ...centeredContentsColumn, key: "questionsCount", label: "Quest\xF5es", width: "100px" }, { ...centeredContentsColumn, key: "studentsCount", label: "Estudantes", width: "100px" }, { ...centeredContentsColumn, key: "performance", label: "Desempenho", width: "160px", render: (_value, row) => /* @__PURE__ */ jsx4(ContentPerformanceCell, { percentage: row.performance.correctPercentage }) } ]; function useSimulatedPerformance({ api, profileName, enabled = true }) { const [searchParams, setSearchParams] = useSearchParams(); const aggregationType = useMemo2( () => getAggregationTypeByProfile(profileName), [profileName] ); const period = useMemo2(() => { const periodParam = searchParams.get("period"); if (periodParam === "7_DAYS") return "7_DAYS"; if (periodParam === "1_MONTH") return "1_MONTH"; if (periodParam === "3_MONTHS") return "3_MONTHS"; if (periodParam === "6_MONTHS") return "6_MONTHS"; if (periodParam === "1_YEAR") return "1_YEAR"; return "1_MONTH"; }, [searchParams]); const scoreType = useMemo2(() => { const scoreTypeParam = searchParams.get("scoreType"); if (scoreTypeParam === "tri") return "tri" /* TRI */; return "percentage" /* PERCENTAGE */; }, [searchParams]); const [selectedAreaKnowledgeId, setSelectedAreaKnowledgeId] = useState7(null); const [selectedAreaRelatedIds, setSelectedAreaRelatedIds] = useState7([]); const [selectedSubjectId, setSelectedSubjectId] = useState7( null ); const [simulatedViewTab, setSimulatedViewTab] = useState7( "students" /* STUDENTS */ ); const [isStudentModalOpen, setIsStudentModalOpen] = useState7(false); const [selectedStudent, setSelectedStudent] = useState7(null); const [isContentModalOpen, setIsContentModalOpen] = useState7(false); const [selectedContent, setSelectedContent] = useState7(null); const [isFiltersModalOpen, setIsFiltersModalOpen] = useState7(false); const [filters, setFilters] = useState7({ schoolIds: [], schoolYearIds: [], classIds: [], studentsIds: [] }); const selectedAreaKnowledgeIdRef = useRef6( selectedAreaKnowledgeId ); const selectedSubjectIdRef = useRef6(selectedSubjectId); const filtersRef = useRef6(filters); const simulatedViewTabRef = useRef6(simulatedViewTab); const scoreTypeRef = useRef6(scoreType); const periodRef = useRef6(period); const aggregationTypeRef = useRef6(aggregationType); useEffect2(() => { selectedAreaKnowledgeIdRef.current = selectedAreaKnowledgeId; }, [selectedAreaKnowledgeId]); useEffect2(() => { selectedSubjectIdRef.current = selectedSubjectId; }, [selectedSubjectId]); useEffect2(() => { filtersRef.current = filters; }, [filters]); useEffect2(() => { simulatedViewTabRef.current = simulatedViewTab; }, [simulatedViewTab]); useEffect2(() => { scoreTypeRef.current = scoreType; }, [scoreType]); useEffect2(() => { periodRef.current = period; }, [period]); useEffect2(() => { aggregationTypeRef.current = aggregationType; }, [aggregationType]); const { data: generalOverviewData, loading: generalOverviewLoading, error: generalOverviewError, fetchOverview: fetchGeneralOverview } = useGeneralOverview(api); const { data: aggregatedData, loading: aggregatedLoading, isRefreshing: aggregatedRefreshing, error: aggregatedError, fetchOverview: fetchAggregatedOverview } = useAggregatedOverview(api); const { data: simulatedData, loading: simulatedLoading, isRefreshing: simulatedRefreshing, error: simulatedError, fetchOverview: fetchSimulatedOverview } = useSimulatedOverview(api); const { data: contentsData, loading: contentsLoading, isRefreshing: contentsRefreshing, error: contentsError, fetchContents } = useSimulatedContents(api); const isEssaySelected = selectedAreaKnowledgeId === ESSAY_AREA_ID; const activeFiltersCount = useMemo2(() => { let count = 0; if (filters.schoolIds.length > 0) count++; if (filters.schoolYearIds.length > 0) count++; if (filters.classIds.length > 0) count++; if (filters.studentsIds.length > 0) count++; return count; }, [filters]); useEffect2(() => { if (!selectedAreaKnowledgeId || !generalOverviewData?.areas) { return; } const area = generalOverviewData.areas.find( (a) => a.id === selectedAreaKnowledgeId ); const relatedIds = area?.relatedIds ?? [selectedAreaKnowledgeId]; setSelectedAreaRelatedIds(relatedIds); }, [generalOverviewData, selectedAreaKnowledgeId]); const studentsTableColumns = useMemo2( () => simulatedTableColumnsBase.map((col) => { if (col.key === "average") { return { ...col, render: (_value, row) => formatScore(row.average, scoreType) }; } if (col.key === "performance") { return { ...col, render: (_value, row) => /* @__PURE__ */ jsx4(SimulatedPerformanceTagCell, { row }) }; } return col; }), [scoreType] ); const loadStudentsData = useCallback7( async (areaKnowledgeId, subjectId, page = 1, refresh = false, periodOverride, scoreTypeOverride) => { const currentFilters = filtersRef.current; const currentPeriod = periodOverride ?? periodRef.current; const currentScoreType = scoreTypeOverride ?? scoreTypeRef.current; const isEssay = areaKnowledgeId === ESSAY_AREA_ID; const effectiveSubjectId = !isEssay && subjectId && subjectId !== "all" ? subjectId : void 0; const effectiveAreaKnowledgeId = !isEssay && areaKnowledgeId && areaKnowledgeId !== "all" ? areaKnowledgeId : void 0; await fetchSimulatedOverview( { simulationType: isEssay ? "essays" : "enem-1", period: currentPeriod, subjectId: effectiveSubjectId, areaKnowledgeId: effectiveAreaKnowledgeId, schoolIds: currentFilters.schoolIds, schoolYearIds: currentFilters.schoolYearIds, classIds: currentFilters.classIds, studentsIds: currentFilters.studentsIds, page, limit: 10, orderBy: "name", order: "asc", scoreType: currentScoreType }, refresh ); }, [fetchSimulatedOverview] ); const loadSkillsData = useCallback7( async (areaKnowledgeId, subjectId, periodOverride, scoreTypeOverride) => { if (areaKnowledgeId === ESSAY_AREA_ID) { return; } const currentFilters = filtersRef.current; const currentPeriod = periodOverride ?? periodRef.current; const currentScoreType = scoreTypeOverride ?? scoreTypeRef.current; const effectiveSubjectId = subjectId && subjectId !== "all" ? subjectId : void 0; const effectiveAreaKnowledgeId = areaKnowledgeId && areaKnowledgeId !== "all" ? areaKnowledgeId : void 0; await fetchContents({ simulationType: "enem-1", period: currentPeriod, subjectId: effectiveSubjectId, areaKnowledgeId: effectiveAreaKnowledgeId, schoolIds: currentFilters.schoolIds, schoolYearIds: currentFilters.schoolYearIds, classIds: currentFilters.classIds, studentsIds: currentFilters.studentsIds, page: 1, limit: 10, orderBy: "correctPercentage", order: "asc", scoreType: currentScoreType }); }, [fetchContents] ); const loadGeneralOverviewData = useCallback7( async (periodOverride, scoreTypeOverride) => { const currentFilters = filtersRef.current; const currentScoreType = scoreTypeOverride ?? scoreTypeRef.current; await fetchGeneralOverview({ period: periodOverride ?? periodRef.current, schoolIds: currentFilters.schoolIds, schoolYearIds: currentFilters.schoolYearIds, classIds: currentFilters.classIds, studentsIds: currentFilters.studentsIds, scoreType: currentScoreType }); }, [fetchGeneralOverview] ); const loadAggregatedOverviewData = useCallback7( async (areaKnowledgeId, subjectId, refresh = false, periodOverride, scoreTypeOverride) => { const currentFilters = filtersRef.current; const currentPeriod = periodOverride ?? periodRef.current; const currentScoreType = scoreTypeOverride ?? scoreTypeRef.current; const currentAggregationType = aggregationTypeRef.current; const isEssay = areaKnowledgeId === ESSAY_AREA_ID; const effectiveSubjectId = !isEssay && subjectId && subjectId !== "all" ? subjectId : void 0; const effectiveAreaKnowledgeId = !isEssay && areaKnowledgeId && areaKnowledgeId !== "all" ? areaKnowledgeId : void 0; await fetchAggregatedOverview( { aggregationType: currentAggregationType, simulationType: isEssay ? "essays" : "enem-1", period: currentPeriod, subjectId: effectiveSubjectId, areaKnowledgeId: effectiveAreaKnowledgeId, schoolIds: currentFilters.schoolIds, schoolYearIds: currentFilters.schoolYearIds, classIds: currentFilters.classIds, studentsIds: currentFilters.studentsIds, scoreType: currentScoreType }, refresh ); }, [fetchAggregatedOverview] ); const handlePeriodChange = useCallback7( (value) => { const newPeriod = value; if (newPeriod === periodRef.current) { return; } const effectiveScoreType = scoreTypeRef.current; setSearchParams((prev) => { const newParams = new URLSearchParams(prev); if (newPeriod === "1_MONTH") { newParams.delete("period"); } else { newParams.set("period", newPeriod); } if (effectiveScoreType === "percentage") { newParams.delete("scoreType"); } else { newParams.set("scoreType", effectiveScoreType); } return newParams; }); loadAggregatedOverviewData( selectedAreaKnowledgeIdRef.current, selectedSubjectIdRef.current, false, newPeriod,