analytica-frontend-lib 1.5.13 → 1.5.15
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/ActivitiesHistory/index.js +5 -5
- package/dist/ActivitiesHistory/index.mjs +4 -4
- package/dist/ActivityCardQuestionBanks/index.js +5 -5
- package/dist/ActivityCardQuestionBanks/index.mjs +4 -4
- package/dist/ActivityCardQuestionPreview/index.js +5 -5
- package/dist/ActivityCardQuestionPreview/index.mjs +4 -4
- package/dist/ActivityCreate/index.js +5 -5
- package/dist/ActivityCreate/index.mjs +4 -4
- package/dist/ActivityDetails/index.js +5 -5
- package/dist/ActivityDetails/index.mjs +4 -4
- package/dist/ActivityFilters/index.js +5 -5
- package/dist/ActivityFilters/index.mjs +4 -4
- package/dist/ActivityPageLayout/index.js +5 -5
- package/dist/ActivityPageLayout/index.mjs +4 -4
- package/dist/ActivityPreview/index.js +5 -5
- package/dist/ActivityPreview/index.mjs +4 -4
- package/dist/AlertManager/index.js +5 -5
- package/dist/AlertManager/index.mjs +4 -4
- package/dist/CheckBoxGroup/index.js +5 -5
- package/dist/CheckBoxGroup/index.mjs +4 -4
- package/dist/DownloadButton/index.d.ts +4 -3
- package/dist/DownloadButton/index.d.ts.map +1 -1
- package/dist/DownloadButton/index.js +8 -2
- package/dist/DownloadButton/index.js.map +1 -1
- package/dist/DownloadButton/index.mjs +7 -1
- package/dist/ExamDetailsLayout/ExamDetailsPage/index.js +5 -5
- package/dist/ExamDetailsLayout/ExamDetailsPage/index.mjs +4 -4
- package/dist/ExamPageLayout/index.js +5 -5
- package/dist/ExamPageLayout/index.mjs +4 -4
- package/dist/PrintableUsersTable/index.js +5 -5
- package/dist/PrintableUsersTable/index.mjs +4 -4
- package/dist/RecommendedLessonCreate/index.js +5 -5
- package/dist/RecommendedLessonCreate/index.mjs +4 -4
- package/dist/RecommendedLessonDetails/index.js +5 -4
- package/dist/RecommendedLessonDetails/index.js.map +1 -1
- package/dist/RecommendedLessonDetails/index.mjs +4 -3
- package/dist/RecommendedLessonsHistory/index.js +5 -5
- package/dist/RecommendedLessonsHistory/index.mjs +4 -4
- package/dist/SendActivityModal/SendActivityModal.js +5 -5
- package/dist/SendActivityModal/SendActivityModal.mjs +4 -4
- package/dist/SendActivityModal/index.js +5 -5
- package/dist/SendActivityModal/index.mjs +4 -4
- package/dist/SendLessonModal/index.js +5 -5
- package/dist/SendLessonModal/index.mjs +4 -4
- package/dist/SimulatedPerformance/SimulatedPerformanceView/index.js +5 -5
- package/dist/SimulatedPerformance/SimulatedPerformanceView/index.mjs +4 -4
- package/dist/SimulationsPage/index.js +5 -5
- package/dist/SimulationsPage/index.mjs +4 -4
- package/dist/TableProvider/index.js +5 -5
- package/dist/TableProvider/index.mjs +4 -4
- package/dist/UnifiedDraftModelPage/index.js +5 -5
- package/dist/UnifiedDraftModelPage/index.mjs +4 -4
- package/dist/UnifiedHistoryPage/index.js +5 -5
- package/dist/UnifiedHistoryPage/index.mjs +4 -4
- package/dist/VideoPlayer/index.js +8 -3
- package/dist/VideoPlayer/index.js.map +1 -1
- package/dist/VideoPlayer/index.mjs +7 -2
- package/dist/chunk-5L6CNJDJ.mjs +223 -0
- package/dist/chunk-5L6CNJDJ.mjs.map +1 -0
- package/dist/{chunk-BYJ7VD2I.mjs → chunk-5SDXK6OI.mjs} +2 -2
- package/dist/chunk-GWEXMU4A.js +223 -0
- package/dist/chunk-GWEXMU4A.js.map +1 -0
- package/dist/{chunk-AEO4XVTH.mjs → chunk-INSEDDUA.mjs} +2 -2
- package/dist/{chunk-5G4M5GTZ.js → chunk-LJEGZ7RW.js} +3 -3
- package/dist/{chunk-5G4M5GTZ.js.map → chunk-LJEGZ7RW.js.map} +1 -1
- package/dist/{chunk-CEY7BXBL.js → chunk-O3XVL3WE.js} +3 -3
- package/dist/{chunk-CEY7BXBL.js.map → chunk-O3XVL3WE.js.map} +1 -1
- package/dist/{chunk-QZU6HKXS.js → chunk-TVQJ4WOR.js} +3 -3
- package/dist/{chunk-QZU6HKXS.js.map → chunk-TVQJ4WOR.js.map} +1 -1
- package/dist/{chunk-6WH6IFNO.mjs → chunk-ZXQ6CGGV.mjs} +2 -2
- package/dist/hooks/useAppContent/index.js +5 -5
- package/dist/hooks/useAppContent/index.mjs +4 -4
- package/dist/index.js +5 -5
- package/dist/index.mjs +4 -4
- package/package.json +2 -1
- package/dist/chunk-2M2RUGQD.mjs +0 -194
- package/dist/chunk-2M2RUGQD.mjs.map +0 -1
- package/dist/chunk-QIF47QNY.js +0 -194
- package/dist/chunk-QIF47QNY.js.map +0 -1
- /package/dist/{chunk-BYJ7VD2I.mjs.map → chunk-5SDXK6OI.mjs.map} +0 -0
- /package/dist/{chunk-AEO4XVTH.mjs.map → chunk-INSEDDUA.mjs.map} +0 -0
- /package/dist/{chunk-6WH6IFNO.mjs.map → chunk-ZXQ6CGGV.mjs.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-QZU6HKXS.js","../src/components/RecommendedLessonDetails/RecommendedLessonDetails.tsx","../src/components/RecommendedLessonDetails/components/Breadcrumb.tsx","../src/components/RecommendedLessonDetails/components/LessonHeader.tsx","../src/components/RecommendedLessonDetails/utils/lessonDetailsUtils.ts","../src/components/RecommendedLessonDetails/components/LoadingSkeleton.tsx","../src/components/RecommendedLessonDetails/components/ResultsSection.tsx","../src/components/RecommendedLessonDetails/components/StudentsTable.tsx","../src/components/RecommendedLessonDetails/components/StudentPerformanceModal.tsx","../src/components/RecommendedLessonDetails/components/StudentActivityPerformanceModal.tsx","../src/components/RecommendedLessonDetails/components/RecommendedLessonViewModal.tsx","../src/components/RecommendedLessonDetails/utils/performanceUtils.ts"],"names":["jsx","jsxs","useMemo","TrophyIcon","WarningIcon","useEffect","useState","LessonAccordionItem","useCallback"],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-QZU6HKXS.js","sourcesContent":[null,"import { useMemo, useState, useCallback } from 'react';\nimport Text from '../Text/Text';\nimport useToastStore from '../Toast/utils/ToastStore';\nimport { cn } from '../../utils/utils';\nimport type { LessonDetailsData } from '../../types/recommendedLessons';\nimport type { SubjectEnum } from '../../enums/SubjectEnum';\nimport type { BaseApiClient } from '../../types/api';\nimport {\n Breadcrumb,\n LessonHeader,\n LoadingSkeleton,\n ResultsSection,\n StudentsTable,\n StudentActivityPerformanceModal,\n RecommendedLessonViewModal,\n} from './components';\nimport { transformStudentForDisplay } from './utils/lessonDetailsUtils';\nimport {\n convertStudentAnswersToPerformanceData,\n type StudentAnswersResponse,\n} from './utils/performanceUtils';\nimport {\n DEFAULT_LABELS,\n type BreadcrumbItem,\n type LessonDetailsLabels,\n type StudentActivityPerformanceData,\n} from './types';\n\n/**\n * Props for RecommendedLessonDetails component\n */\nexport interface RecommendedLessonDetailsProps {\n /** Lesson data to display (from API responses) */\n data: LessonDetailsData | null;\n /** Loading state */\n loading?: boolean;\n /** Error message */\n error?: string | null;\n /** Callback when \"Ver aula\" button is clicked */\n onViewLesson?: () => void;\n /**\n * API client for making requests.\n * When provided, enables the \"Corrigir atividade\" functionality.\n */\n apiClient?: BaseApiClient;\n /** Callback for breadcrumb navigation */\n onBreadcrumbClick?: (path: string) => void;\n /** Function to map subject name to SubjectEnum */\n mapSubjectNameToEnum?: (name: string) => SubjectEnum | null;\n /** Custom breadcrumb items */\n breadcrumbs?: BreadcrumbItem[];\n /** Custom labels */\n labels?: Partial<LessonDetailsLabels>;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * RecommendedLessonDetails component\n *\n * Displays detailed information about a recommended lesson including:\n * - Breadcrumb navigation\n * - Lesson metadata (title, dates, school, subject, class)\n * - Results cards (completion %, best result topic, hardest topic)\n * - Students table with status, progress, and actions\n *\n * @example\n * ```tsx\n * <RecommendedLessonDetails\n * data={{\n * recommendedClass: recommendedClassData, // from /recommendedClass/{id}\n * details: detailsData, // from /recommendedClass/{id}/details\n * breakdown: breakdown, // optional, from /recommended-class/history\n * }}\n * apiClient={api}\n * onViewLesson={() => navigate('/view-lesson')}\n * mapSubjectNameToEnum={mapSubjectNameToEnum}\n * />\n * ```\n */\nconst RecommendedLessonDetails = ({\n data,\n loading = false,\n error = null,\n onViewLesson,\n apiClient,\n onBreadcrumbClick,\n mapSubjectNameToEnum,\n breadcrumbs,\n labels: customLabels,\n className,\n}: RecommendedLessonDetailsProps) => {\n const labels = useMemo(\n () => ({ ...DEFAULT_LABELS, ...customLabels }),\n [customLabels]\n );\n\n // Toast store for notifications\n const addToast = useToastStore((state) => state.addToast);\n\n // Activity performance modal state\n const [performanceModalOpen, setPerformanceModalOpen] = useState(false);\n const [performanceData, setPerformanceData] =\n useState<StudentActivityPerformanceData | null>(null);\n const [performanceLoading, setPerformanceLoading] = useState(false);\n const [performanceError, setPerformanceError] = useState<string | null>(null);\n\n // View lesson modal state\n const [viewLessonModalOpen, setViewLessonModalOpen] = useState(false);\n\n /**\n * Handle correct activity action\n * Fetches all activities and lessons for the student and opens the performance modal\n */\n const handleCorrectActivity = useCallback(\n async (studentId: string) => {\n if (!apiClient || !data?.recommendedClass.id) return;\n\n // Find student from data\n const student = data?.details.students.find(\n (s) => s.userInstitutionId === studentId\n );\n\n // Guard: ensure student and userId are valid before proceeding\n if (!student?.userId) {\n addToast({\n title: 'Erro ao carregar aluno',\n description: 'Não foi possível identificar o aluno. Tente novamente.',\n variant: 'solid',\n action: 'warning',\n position: 'top-right',\n });\n return;\n }\n\n const studentName = student.name;\n const userId = student.userId;\n\n setPerformanceModalOpen(true);\n setPerformanceLoading(true);\n setPerformanceData(null);\n setPerformanceError(null);\n\n try {\n // Fetch all answers for the student in this recommended class\n const response = await apiClient.get<StudentAnswersResponse>(\n `/recommended-class/${data.recommendedClass.id}/student/${studentId}/answers`\n );\n\n // Convert API response to performance data format\n const performanceData = convertStudentAnswersToPerformanceData(\n response.data,\n studentId,\n userId,\n studentName\n );\n\n setPerformanceData(performanceData);\n } catch (err) {\n console.error('Error fetching activity performance data:', err);\n setPerformanceError(\n err instanceof Error\n ? err.message\n : 'Erro ao carregar desempenho do aluno'\n );\n } finally {\n setPerformanceLoading(false);\n }\n },\n [apiClient, data?.recommendedClass.id, data?.details.students, addToast]\n );\n\n /**\n * Handle close performance modal\n */\n const handleClosePerformanceModal = useCallback(() => {\n setPerformanceModalOpen(false);\n setPerformanceData(null);\n setPerformanceError(null);\n }, []);\n\n /**\n * Handle view lesson action\n * If onViewLesson prop is provided, call it; otherwise open internal modal\n */\n const handleViewLesson = useCallback(() => {\n if (onViewLesson) {\n onViewLesson();\n } else {\n setViewLessonModalOpen(true);\n }\n }, [onViewLesson]);\n\n /**\n * Handle close view lesson modal\n */\n const handleCloseViewLessonModal = useCallback(() => {\n setViewLessonModalOpen(false);\n }, []);\n\n const defaultBreadcrumbs: BreadcrumbItem[] = useMemo(\n () => [\n { label: 'Aulas recomendadas', path: '/aulas-recomendadas' },\n { label: data?.recommendedClass.title || 'Detalhes' },\n ],\n [data?.recommendedClass.title]\n );\n\n const breadcrumbItems = breadcrumbs || defaultBreadcrumbs;\n\n // Transform API students to display format\n const displayStudents = useMemo(() => {\n if (!data?.details.students) return [];\n const deadline = data?.recommendedClass.finalDate;\n return data.details.students.map((student) =>\n transformStudentForDisplay(student, deadline)\n );\n }, [data?.details.students, data?.recommendedClass.finalDate]);\n\n if (loading) {\n return (\n <div\n className={cn('flex flex-col gap-6', className)}\n data-testid=\"lesson-details-loading\"\n >\n <LoadingSkeleton />\n </div>\n );\n }\n\n if (error) {\n return (\n <div\n className={cn(\n 'flex flex-col items-center justify-center py-12',\n className\n )}\n data-testid=\"lesson-details-error\"\n >\n <Text size=\"md\" className=\"text-error-700\">\n {error}\n </Text>\n </div>\n );\n }\n\n if (!data) {\n return null;\n }\n\n return (\n <>\n <div\n className={cn('flex flex-col gap-6', className)}\n data-testid=\"recommended-class-details\"\n >\n {/* Breadcrumb */}\n <Breadcrumb items={breadcrumbItems} onItemClick={onBreadcrumbClick} />\n\n {/* Header with metadata */}\n <LessonHeader\n data={data}\n onViewLesson={handleViewLesson}\n mapSubjectNameToEnum={mapSubjectNameToEnum}\n viewLessonLabel={labels.viewLesson}\n />\n\n {/* Results section */}\n <ResultsSection data={data} labels={labels} />\n\n {/* Students table */}\n <StudentsTable\n students={displayStudents}\n onCorrectActivity={apiClient ? handleCorrectActivity : undefined}\n labels={labels}\n />\n </div>\n\n {/* Student Activity Performance Modal */}\n {apiClient && (\n <StudentActivityPerformanceModal\n isOpen={performanceModalOpen}\n onClose={handleClosePerformanceModal}\n data={performanceData}\n loading={performanceLoading}\n error={performanceError}\n apiClient={apiClient}\n />\n )}\n\n {/* View Lesson Modal */}\n <RecommendedLessonViewModal\n isOpen={viewLessonModalOpen}\n onClose={handleCloseViewLessonModal}\n data={data}\n apiClient={apiClient}\n />\n </>\n );\n};\n\nexport default RecommendedLessonDetails;\n","import { CaretRightIcon } from '@phosphor-icons/react';\nimport Text from '../../Text/Text';\nimport type { BreadcrumbItem } from '../types';\n\n/**\n * Props for Breadcrumb component\n */\ninterface BreadcrumbProps {\n /** Breadcrumb items to display */\n items: BreadcrumbItem[];\n /** Callback when a breadcrumb item is clicked */\n onItemClick?: (path: string) => void;\n}\n\n/**\n * Breadcrumb navigation component\n * Displays a path of navigation items with optional click handlers\n */\nexport const Breadcrumb = ({ items, onItemClick }: BreadcrumbProps) => (\n <nav className=\"flex items-center gap-2 text-sm\" aria-label=\"Breadcrumb\">\n {items.map((item, index) => (\n <Text\n key={item.path ?? item.label}\n as=\"span\"\n className=\"flex items-center gap-2\"\n >\n {index > 0 && <CaretRightIcon size={14} className=\"text-text-500\" />}\n {item.path ? (\n <button\n type=\"button\"\n onClick={() => onItemClick?.(item.path!)}\n className=\"text-text-600 hover:text-primary-700 transition-colors\"\n >\n {item.label}\n </button>\n ) : (\n <Text as=\"span\" className=\"text-text-950 font-medium\">\n {item.label}\n </Text>\n )}\n </Text>\n ))}\n </nav>\n);\n\nexport default Breadcrumb;\n","import { BookBookmarkIcon } from '@phosphor-icons/react';\nimport Text from '../../Text/Text';\nimport Button from '../../Button/Button';\nimport { getSubjectInfo } from '../../SubjectInfo/SubjectInfo';\nimport { cn } from '../../../utils/utils';\nimport type { LessonDetailsData } from '../../../types/recommendedLessons';\nimport type { SubjectEnum } from '../../../enums/SubjectEnum';\nimport { formatDate } from '../utils/lessonDetailsUtils';\n\n/**\n * Props for LessonHeader component\n */\ninterface LessonHeaderProps {\n /** Lesson details data */\n data: LessonDetailsData;\n /** Callback when \"Ver aula\" button is clicked */\n onViewLesson?: () => void;\n /** Function to map subject name to SubjectEnum */\n mapSubjectNameToEnum?: (name: string) => SubjectEnum | null;\n /** Label for the view lesson button */\n viewLessonLabel: string;\n}\n\n/**\n * Header section with lesson metadata\n * Displays title, dates, school, subject, and class information\n */\nexport const LessonHeader = ({\n data,\n onViewLesson,\n mapSubjectNameToEnum,\n viewLessonLabel,\n}: LessonHeaderProps) => {\n const { recommendedClass, breakdown } = data;\n\n // Extract subject from first lesson if available\n const subjectName =\n recommendedClass.lessons[0]?.supLessonsProgress?.lesson?.subject?.name ||\n '';\n const subjectEnum = mapSubjectNameToEnum?.(subjectName);\n const subjectInfo = subjectEnum ? getSubjectInfo(subjectEnum) : null;\n\n return (\n <div className=\"bg-background rounded-xl border border-border-50 p-6\">\n <div className=\"flex flex-col lg:flex-row lg:items-start lg:justify-between gap-4\">\n <div className=\"flex flex-col gap-2\">\n <Text\n as=\"h1\"\n size=\"xl\"\n weight=\"bold\"\n className=\"text-text-950 lg:text-2xl\"\n >\n {recommendedClass.title}\n </Text>\n <div className=\"flex flex-wrap items-center gap-2 text-sm text-text-600\">\n <Text as=\"span\" size=\"sm\" className=\"text-text-600\">\n Início em {formatDate(recommendedClass.startDate)}\n </Text>\n <Text as=\"span\" size=\"sm\" className=\"text-text-400\">\n •\n </Text>\n <Text as=\"span\" size=\"sm\" className=\"text-text-600\">\n Prazo final {formatDate(recommendedClass.finalDate)}\n </Text>\n {breakdown?.schoolName && (\n <>\n <Text as=\"span\" size=\"sm\" className=\"text-text-400\">\n •\n </Text>\n <Text as=\"span\" size=\"sm\" className=\"text-text-600\">\n {breakdown.schoolName}\n </Text>\n </>\n )}\n {subjectName && (\n <>\n <Text as=\"span\" size=\"sm\" className=\"text-text-400\">\n •\n </Text>\n <Text as=\"span\" size=\"sm\" className=\"flex items-center gap-1\">\n {subjectInfo && (\n <Text\n as=\"span\"\n className={cn(\n 'size-5 rounded flex items-center justify-center text-white',\n subjectInfo.colorClass\n )}\n >\n {subjectInfo.icon}\n </Text>\n )}\n {subjectName}\n </Text>\n </>\n )}\n {breakdown?.className && (\n <>\n <Text as=\"span\" size=\"sm\" className=\"text-text-400\">\n •\n </Text>\n <Text as=\"span\" size=\"sm\" className=\"text-text-600\">\n {breakdown.className}\n </Text>\n </>\n )}\n </div>\n </div>\n {onViewLesson && (\n <Button\n variant=\"solid\"\n action=\"primary\"\n size=\"small\"\n iconLeft={<BookBookmarkIcon size={16} />}\n onClick={onViewLesson}\n >\n {viewLessonLabel}\n </Button>\n )}\n </div>\n </div>\n );\n};\n\nexport default LessonHeader;\n","import type { RecommendedClassDetailStudent } from '../../../types/recommendedLessons';\nimport {\n deriveStudentStatus,\n formatDaysToComplete,\n} from '../../../types/recommendedLessons';\nimport type { DisplayStudent } from '../types';\n\n/**\n * Format date string to display format (DD/MM/YYYY)\n * @param dateString - ISO date string or null\n * @returns Formatted date string or placeholder\n */\nexport const formatDate = (dateString: string | null): string => {\n if (!dateString) return '-';\n const date = new Date(dateString);\n if (Number.isNaN(date.getTime())) return '-';\n return date.toLocaleDateString('pt-BR', {\n day: '2-digit',\n month: '2-digit',\n year: 'numeric',\n });\n};\n\n/**\n * Transform API student data to display format\n * @param student - Student data from API\n * @param deadline - RecommendedClass deadline to determine NAO_FINALIZADO status\n * @returns Transformed student for table display\n */\nexport const transformStudentForDisplay = (\n student: RecommendedClassDetailStudent,\n deadline?: string | null\n): DisplayStudent => ({\n id: student.userInstitutionId,\n name: student.name,\n status: deriveStudentStatus(student.progress, student.completedAt, deadline),\n completionPercentage: student.progress,\n duration: formatDaysToComplete(student.daysToComplete),\n});\n","import {\n SkeletonText,\n SkeletonRounded,\n SkeletonTable,\n} from '../../Skeleton/Skeleton';\n\n/**\n * Loading skeleton component for RecommendedLessonDetails\n * Uses the shared Skeleton components from the library\n */\nexport const LoadingSkeleton = () => (\n <div className=\"flex flex-col gap-6\">\n {/* Breadcrumb skeleton */}\n <SkeletonText width={256} />\n\n {/* Header skeleton */}\n <div className=\"bg-background rounded-xl border border-border-50 p-6\">\n <div className=\"flex flex-col gap-3\">\n <SkeletonText width=\"75%\" height={28} />\n <SkeletonText width=\"50%\" />\n </div>\n </div>\n\n {/* Results section skeleton */}\n <div className=\"flex flex-col gap-4\">\n <SkeletonText width={192} height={20} />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <SkeletonRounded height={140} />\n <SkeletonRounded height={140} />\n <SkeletonRounded height={140} />\n </div>\n </div>\n\n {/* Table skeleton */}\n <div className=\"bg-background rounded-xl border border-border-50 p-4\">\n <SkeletonTable rows={4} columns={5} />\n </div>\n </div>\n);\n\nexport default LoadingSkeleton;\n","import { TrophyIcon, WarningIcon } from '@phosphor-icons/react';\nimport Text from '../../Text/Text';\nimport ProgressCircle from '../../ProgressCircle/ProgressCircle';\nimport type { LessonDetailsData } from '../../../types/recommendedLessons';\nimport type { LessonDetailsLabels } from '../types';\n\n/**\n * Props for ResultsSection component\n */\ninterface ResultsSectionProps {\n /** Lesson details data */\n data: LessonDetailsData;\n /** Labels for the component */\n labels: LessonDetailsLabels;\n}\n\n/**\n * Results section with 3 cards\n * Displays completion percentage, best result, and hardest topic\n */\nexport const ResultsSection = ({ data, labels }: ResultsSectionProps) => {\n const { details } = data;\n const { aggregated, contentPerformance } = details;\n\n return (\n <div className=\"flex flex-col gap-4\">\n <Text as=\"h2\" size=\"md\" weight=\"semibold\" className=\"text-text-950\">\n {labels.resultsTitle}\n </Text>\n {/* White container with the 3 cards */}\n <div className=\"bg-background rounded-xl border border-border-50 p-4\">\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n {/* Completion percentage card */}\n <div className=\"flex flex-col items-center justify-center rounded-xl p-4 min-h-28 bg-primary-50\">\n <ProgressCircle\n value={aggregated.completionPercentage}\n size=\"small\"\n variant=\"blue\"\n label={labels.completedLabel}\n showPercentage\n />\n </div>\n\n {/* Best result topic card */}\n <div className=\"flex flex-col items-center justify-center rounded-xl p-4 min-h-28 bg-success-200\">\n <Text\n as=\"span\"\n className=\"size-8 rounded-full flex items-center justify-center bg-warning-300 mb-2\"\n >\n <TrophyIcon size={18} weight=\"fill\" className=\"text-white\" />\n </Text>\n <Text\n size=\"2xs\"\n weight=\"bold\"\n className=\"text-text-700 uppercase text-center leading-none mb-1\"\n >\n {labels.bestResultLabel}\n </Text>\n <Text\n size=\"xl\"\n weight=\"bold\"\n className=\"text-success-700 text-center leading-none tracking-wide\"\n >\n {contentPerformance.best?.contentName || '-'}\n </Text>\n </div>\n\n {/* Hardest topic card */}\n <div className=\"flex flex-col items-center justify-center rounded-xl p-4 min-h-28 bg-error-100\">\n <Text\n as=\"span\"\n className=\"size-8 rounded-full flex items-center justify-center bg-error-300 mb-2\"\n >\n <WarningIcon size={18} weight=\"fill\" className=\"text-error-700\" />\n </Text>\n <Text\n size=\"2xs\"\n weight=\"bold\"\n className=\"text-text-700 uppercase text-center leading-none mb-1\"\n >\n {labels.hardestTopicLabel}\n </Text>\n <Text\n size=\"xl\"\n weight=\"bold\"\n className=\"text-error-700 text-center leading-none tracking-wide\"\n >\n {contentPerformance.worst?.contentName || '-'}\n </Text>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default ResultsSection;\n","import { UserIcon } from '@phosphor-icons/react';\nimport Text from '../../Text/Text';\nimport { TruncatedText } from '../../TruncatedText/TruncatedText';\nimport Button from '../../Button/Button';\nimport Badge from '../../Badge/Badge';\nimport ProgressBar from '../../ProgressBar/ProgressBar';\nimport Table, {\n TableHeader,\n TableBody,\n TableHead,\n TableRow,\n TableCell,\n useTableSort,\n} from '../../Table/Table';\nimport { getStudentStatusBadgeAction } from '../../../types/recommendedLessons';\nimport type { DisplayStudent, LessonDetailsLabels } from '../types';\n\n/**\n * Props for StudentsTable component\n */\ninterface StudentsTableProps {\n /** List of students to display */\n students: DisplayStudent[];\n /** Callback when correct activity is clicked */\n onCorrectActivity?: (studentId: string) => void;\n /** Labels for the component */\n labels: LessonDetailsLabels;\n /** Empty state message */\n emptyMessage?: string;\n}\n\n/**\n * Students table component\n * Displays students with their status, completion, and duration\n */\nexport const StudentsTable = ({\n students,\n onCorrectActivity,\n labels,\n emptyMessage = 'Nenhum aluno encontrado',\n}: StudentsTableProps) => {\n const { sortedData, sortColumn, sortDirection, handleSort } =\n useTableSort<DisplayStudent>(students);\n\n return (\n <div className=\"bg-background rounded-xl border border-border-50 overflow-hidden\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead\n sortable\n sortDirection={sortColumn === 'name' ? sortDirection : undefined}\n onSort={() => handleSort('name')}\n >\n {labels.studentColumn}\n </TableHead>\n <TableHead\n sortable\n sortDirection={\n sortColumn === 'status' ? sortDirection : undefined\n }\n onSort={() => handleSort('status')}\n >\n {labels.statusColumn}\n </TableHead>\n <TableHead\n sortable\n sortDirection={\n sortColumn === 'completionPercentage'\n ? sortDirection\n : undefined\n }\n onSort={() => handleSort('completionPercentage')}\n >\n {labels.completionColumn}\n </TableHead>\n <TableHead>{labels.durationColumn}</TableHead>\n <TableHead className=\"w-[160px]\" />\n </TableRow>\n </TableHeader>\n <TableBody>\n {sortedData.length === 0 ? (\n <TableRow>\n <TableCell colSpan={5} className=\"text-center py-8\">\n <Text size=\"sm\" className=\"text-text-500\">\n {emptyMessage}\n </Text>\n </TableCell>\n </TableRow>\n ) : (\n sortedData.map((student) => (\n <TableRow key={student.id}>\n <TableCell className=\"max-w-[220px]\">\n <div className=\"flex items-center gap-2 min-w-0\">\n <Text\n as=\"span\"\n className=\"size-8 rounded-full bg-background-100 flex items-center justify-center shrink-0\"\n >\n <UserIcon size={16} className=\"text-text-500\" />\n </Text>\n <TruncatedText size=\"sm\" color=\"text-text-950\">\n {student.name}\n </TruncatedText>\n </div>\n </TableCell>\n <TableCell>\n <Badge\n variant=\"solid\"\n action={getStudentStatusBadgeAction(student.status)}\n size=\"small\"\n >\n {student.status}\n </Badge>\n </TableCell>\n <TableCell>\n <div className=\"flex flex-col gap-1 min-w-[120px]\">\n <Text size=\"sm\" className=\"text-primary-700 font-medium\">\n {student.completionPercentage}%\n </Text>\n <ProgressBar\n value={student.completionPercentage}\n size=\"small\"\n variant=\"blue\"\n className=\"w-full max-w-[100px]\"\n />\n </div>\n </TableCell>\n <TableCell>\n <Text size=\"sm\" className=\"text-text-700\">\n {student.duration ?? '-'}\n </Text>\n </TableCell>\n <TableCell>\n {onCorrectActivity && (\n <Button\n variant=\"outline\"\n size=\"extra-small\"\n onClick={() => onCorrectActivity(student.id)}\n >\n {labels.correctActivity}\n </Button>\n )}\n </TableCell>\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </div>\n );\n};\n\nexport default StudentsTable;\n","import { useMemo, type ReactNode } from 'react';\nimport { LightbulbFilamentIcon } from '@phosphor-icons/react/dist/csr/LightbulbFilament';\nimport { WarningCircleIcon } from '@phosphor-icons/react/dist/csr/WarningCircle';\nimport { UserIcon } from '@phosphor-icons/react/dist/csr/User';\nimport { CheckCircleIcon } from '@phosphor-icons/react/dist/csr/CheckCircle';\nimport { XCircleIcon } from '@phosphor-icons/react/dist/csr/XCircle';\nimport Modal from '../../Modal/Modal';\nimport Text from '../../Text/Text';\nimport Badge from '../../Badge/Badge';\nimport ProgressBar from '../../ProgressBar/ProgressBar';\nimport { CardAccordation } from '../../Accordation/Accordation';\nimport {\n AlternativesList,\n type Alternative,\n} from '../../Alternative/Alternative';\nimport { OptionStatus } from '../../../enums/Options';\nimport type {\n StudentPerformanceData,\n StudentPerformanceLabels,\n LessonProgress,\n LessonQuestion,\n} from '../types';\nimport { DEFAULT_PERFORMANCE_LABELS } from '../types';\nimport { HtmlMathRenderer } from '../../HtmlMathRenderer';\n\n/**\n * Props for StudentPerformanceModal component\n */\nexport interface StudentPerformanceModalProps {\n /** Whether the modal is open */\n isOpen: boolean;\n /** Callback to close the modal */\n onClose: () => void;\n /** Student performance data */\n data: StudentPerformanceData | null;\n /** Loading state */\n loading?: boolean;\n /** Error message */\n error?: string | null;\n /** Custom labels */\n labels?: Partial<StudentPerformanceLabels>;\n}\n\n/**\n * Performance card with colored header and white footer section\n * Matches the Figma design with icon, label, value on top and secondary info below\n */\nconst PerformanceCard = ({\n icon,\n label,\n value,\n secondaryLabel,\n secondaryValue,\n variant,\n}: {\n icon: ReactNode;\n label: string;\n value: string | number;\n secondaryLabel: string;\n secondaryValue: string | null;\n variant: 'success' | 'error';\n}) => {\n const headerBgColor = {\n success: 'bg-success-200',\n error: 'bg-error-100',\n }[variant];\n\n const valueColor = {\n success: 'text-success-700',\n error: 'text-error-700',\n }[variant];\n\n const iconBgColor = {\n success: 'bg-warning-300',\n error: 'bg-error-300',\n }[variant];\n\n const secondaryLabelColor = {\n success: 'text-success-600',\n error: 'text-error-600',\n }[variant];\n\n return (\n <div className=\"flex flex-col rounded-xl border border-border-50 bg-background overflow-hidden\">\n {/* Header section with colored background */}\n <div\n className={`flex flex-col items-center justify-center p-4 gap-1 ${headerBgColor}`}\n >\n <Text\n as=\"span\"\n className={`size-8 rounded-full flex items-center justify-center ${iconBgColor}`}\n >\n {icon}\n </Text>\n <Text\n size=\"2xs\"\n weight=\"medium\"\n className=\"text-text-800 uppercase text-center leading-none\"\n >\n {label}\n </Text>\n <Text size=\"xl\" weight=\"bold\" className={`${valueColor} text-center`}>\n {value}\n </Text>\n </div>\n\n {/* Footer section with white background */}\n <div className=\"flex flex-col items-center gap-2 px-4 py-3\">\n <Text\n size=\"2xs\"\n weight=\"medium\"\n className={`${secondaryLabelColor} uppercase text-center`}\n >\n {secondaryLabel}\n </Text>\n <Badge size=\"large\" action=\"info\">\n {secondaryValue || '-'}\n </Badge>\n </div>\n </div>\n );\n};\n\n/**\n * Determine the status of an alternative based on correctness and selection\n */\nconst getAlternativeStatus = (\n isCorrect: boolean,\n isSelected: boolean\n): OptionStatus | undefined => {\n if (isCorrect) {\n return OptionStatus.CORRECT;\n }\n if (isSelected) {\n return OptionStatus.INCORRECT;\n }\n return undefined;\n};\n\n/**\n * Convert question alternatives to AlternativesList format\n */\nconst mapAlternatives = (question: LessonQuestion): Alternative[] => {\n return question.alternatives.map((alt) => ({\n value: alt.id,\n label: alt.text,\n status: getAlternativeStatus(alt.isCorrect, alt.isSelected),\n }));\n};\n\n/**\n * Get selected alternative value from question\n */\nconst getSelectedValue = (question: LessonQuestion): string | undefined => {\n const selected = question.alternatives.find((alt) => alt.isSelected);\n return selected?.id;\n};\n\n/**\n * Question accordion item inside a lesson\n */\nconst QuestionAccordionItem = ({\n question,\n index,\n}: {\n question: LessonQuestion;\n index: number;\n}) => (\n <CardAccordation\n value={question.id}\n className=\"bg-background rounded-xl border border-border-50\"\n triggerClassName=\"py-5 px-5\"\n contentClassName=\"px-5 pb-5\"\n trigger={\n <div className=\"flex items-center justify-between flex-1\">\n <Text size=\"sm\" weight=\"semibold\" className=\"text-text-950\">\n Questão {index + 1}\n </Text>\n <Badge\n size=\"small\"\n action={question.isCorrect ? 'success' : 'error'}\n variant=\"solid\"\n iconLeft={question.isCorrect ? <CheckCircleIcon /> : <XCircleIcon />}\n >\n {question.isCorrect ? 'Correta' : 'Incorreta'}\n </Badge>\n </div>\n }\n >\n <div className=\"flex flex-col gap-3\">\n {/* Question statement */}\n <HtmlMathRenderer\n content={question.statement}\n className=\"text-sm text-text-700\"\n />\n\n {/* Alternatives accordion */}\n <CardAccordation\n value={`${question.id}-alternatives`}\n className=\"bg-background rounded-lg border border-border-50\"\n triggerClassName=\"py-5 px-5\"\n contentClassName=\"px-5 py-5\"\n trigger={\n <Text size=\"sm\" weight=\"medium\" className=\"text-text-800\">\n Alternativas\n </Text>\n }\n >\n <AlternativesList\n mode=\"readonly\"\n alternatives={mapAlternatives(question)}\n selectedValue={getSelectedValue(question)}\n layout=\"default\"\n />\n </CardAccordation>\n </div>\n </CardAccordation>\n);\n\n/**\n * Lesson accordion item\n */\nconst LessonAccordionItem = ({ lesson }: { lesson: LessonProgress }) => (\n <CardAccordation\n value={lesson.id}\n className=\"bg-background rounded-xl border border-border-50\"\n triggerClassName=\"py-5 px-5\"\n contentClassName=\"px-5 pb-5\"\n trigger={\n <div className=\"flex flex-col gap-1 flex-1\">\n <Text size=\"sm\" weight=\"semibold\" className=\"text-text-950\">\n {lesson.title}\n </Text>\n <ProgressBar\n value={lesson.progress}\n variant=\"blue\"\n size=\"medium\"\n showPercentage\n layout=\"default\"\n />\n </div>\n }\n >\n <div className=\"flex flex-col gap-2\">\n {lesson.questions.map((question, index) => (\n <QuestionAccordionItem\n key={question.id}\n question={question}\n index={index}\n />\n ))}\n </div>\n </CardAccordation>\n);\n\n/**\n * StudentPerformanceModal component\n *\n * Displays a modal with student performance details including:\n * - Student name with search icon\n * - Two performance cards (correct answers, incorrect answers)\n * - Expandable list of lessons with progress\n *\n * @example\n * ```tsx\n * <StudentPerformanceModal\n * isOpen={isModalOpen}\n * onClose={() => setIsModalOpen(false)}\n * data={{\n * studentName: 'Lucas Oliveira',\n * correctAnswers: 8,\n * incorrectAnswers: 7,\n * bestResult: 'Fotossíntese',\n * hardestTopic: 'Células',\n * lessons: [\n * { id: '1', title: 'Categorias Taxonômicas', progress: 50 },\n * ],\n * }}\n * />\n * ```\n */\n/**\n * Loading skeleton for the modal content\n */\nconst LoadingSkeleton = () => (\n <div className=\"flex flex-col gap-4 animate-pulse\">\n <div className=\"h-6 bg-background-200 rounded w-48\" />\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"h-44 bg-background-200 rounded-xl\" />\n <div className=\"h-44 bg-background-200 rounded-xl\" />\n </div>\n </div>\n);\n\n/**\n * Error state component\n */\nconst ErrorContent = ({ message }: { message: string }) => (\n <div className=\"flex flex-col items-center justify-center py-8 gap-3\">\n <Text\n as=\"span\"\n className=\"size-12 rounded-full bg-error-100 flex items-center justify-center\"\n >\n <WarningCircleIcon size={24} className=\"text-error-700\" />\n </Text>\n <Text size=\"md\" className=\"text-error-700 text-center\">\n {message}\n </Text>\n </div>\n);\n\n/**\n * Performance content when data is available\n */\nconst PerformanceContent = ({\n data,\n labels,\n}: {\n data: StudentPerformanceData;\n labels: StudentPerformanceLabels;\n}) => (\n <div className=\"flex flex-col gap-5\">\n {/* Student name with icon */}\n <div className=\"flex items-center gap-2\">\n <Text\n as=\"span\"\n className=\"size-8 rounded-full bg-background-100 flex items-center justify-center\"\n >\n <UserIcon size={16} className=\"text-text-500\" />\n </Text>\n <Text size=\"md\" weight=\"medium\" className=\"text-text-950\">\n {data.studentName}\n </Text>\n </div>\n\n {/* Two performance cards in a row */}\n <div className=\"grid grid-cols-2 gap-3\">\n {/* Correct answers card */}\n <PerformanceCard\n icon={\n <LightbulbFilamentIcon\n size={18}\n weight=\"fill\"\n className=\"text-white\"\n />\n }\n label={labels.correctAnswersLabel}\n value={data.correctAnswers}\n secondaryLabel={labels.bestResultLabel}\n secondaryValue={data.bestResult}\n variant=\"success\"\n />\n\n {/* Incorrect answers card */}\n <PerformanceCard\n icon={\n <WarningCircleIcon\n size={18}\n weight=\"fill\"\n className=\"text-error-700\"\n />\n }\n label={labels.incorrectAnswersLabel}\n value={data.incorrectAnswers}\n secondaryLabel={labels.hardestTopicLabel}\n secondaryValue={data.hardestTopic}\n variant=\"error\"\n />\n </div>\n\n {/* Lessons section */}\n {data.lessons.length > 0 && (\n <div className=\"flex flex-col gap-3\">\n <Text size=\"md\" weight=\"semibold\" className=\"text-text-950\">\n {labels.lessonsTitle}\n </Text>\n <div className=\"flex flex-col gap-2\">\n {data.lessons.map((lesson) => (\n <LessonAccordionItem key={lesson.id} lesson={lesson} />\n ))}\n </div>\n </div>\n )}\n </div>\n);\n\n/**\n * Renders the modal content based on loading, error, and data state\n */\nconst renderModalContent = (\n loading: boolean,\n error: string | null | undefined,\n data: StudentPerformanceData | null,\n labels: StudentPerformanceLabels\n): ReactNode => {\n if (loading) {\n return <LoadingSkeleton />;\n }\n\n if (error) {\n return <ErrorContent message={error} />;\n }\n\n if (data) {\n return <PerformanceContent data={data} labels={labels} />;\n }\n\n return null;\n};\n\nexport const StudentPerformanceModal = ({\n isOpen,\n onClose,\n data,\n loading = false,\n error = null,\n labels: customLabels,\n}: StudentPerformanceModalProps) => {\n const labels = useMemo(\n () => ({ ...DEFAULT_PERFORMANCE_LABELS, ...customLabels }),\n [customLabels]\n );\n\n if (!data && !loading && !error) {\n return null;\n }\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={onClose}\n title={labels.title}\n size=\"lg\"\n contentClassName=\"max-h-[70vh] overflow-y-auto\"\n >\n {renderModalContent(loading, error, data, labels)}\n </Modal>\n );\n};\n\nexport default StudentPerformanceModal;\n","import {\n useMemo,\n useState,\n useCallback,\n useEffect,\n type ReactNode,\n} from 'react';\nimport { TimerIcon } from '@phosphor-icons/react/dist/csr/Timer';\nimport { TrophyIcon } from '@phosphor-icons/react/dist/csr/Trophy';\nimport { WarningIcon } from '@phosphor-icons/react/dist/csr/Warning';\nimport { UserIcon } from '@phosphor-icons/react/dist/csr/User';\nimport { WarningCircleIcon } from '@phosphor-icons/react/dist/csr/WarningCircle';\nimport type { Icon } from '@phosphor-icons/react';\nimport Modal from '../../Modal/Modal';\nimport Text from '../../Text/Text';\nimport Badge from '../../Badge/Badge';\nimport Button from '../../Button/Button';\nimport Radio from '../../Radio/Radio';\nimport TextArea from '../../TextArea/TextArea';\nimport ProgressBar from '../../ProgressBar/ProgressBar';\nimport { CardAccordation, AccordionGroup } from '../../Accordation';\nimport {\n AlternativesList,\n type Alternative,\n} from '../../Alternative/Alternative';\nimport { OptionStatus } from '../../../enums/Options';\nimport useToastStore from '../../Toast/utils/ToastStore';\nimport { StatCard } from '../../shared/StatCard';\nimport type { BaseApiClient } from '../../../types/api';\nimport type {\n StudentActivityPerformanceData,\n StudentActivityPerformanceLabels,\n PerformanceActivity,\n PerformanceLesson,\n LessonQuestion,\n} from '../types';\nimport { DEFAULT_ACTIVITY_PERFORMANCE_LABELS } from '../types';\nimport { cn } from '../../../utils/utils';\nimport { HtmlMathRenderer } from '../../HtmlMathRenderer';\n\n/** Field names for essay correction state updates */\nconst EssayCorrectionField = {\n IsCorrect: 'isCorrect',\n TeacherFeedback: 'teacherFeedback',\n} as const;\n\n/**\n * Essay correction state for a question\n */\ninterface EssayCorrectionState {\n isCorrect: boolean | null;\n teacherFeedback: string;\n isSaving: boolean;\n isSaved: boolean;\n}\n\n/**\n * Props for StudentActivityPerformanceModal component\n */\nexport interface StudentActivityPerformanceModalProps {\n /** Whether the modal is open */\n isOpen: boolean;\n /** Callback to close the modal */\n onClose: () => void;\n /** Student activity performance data */\n data: StudentActivityPerformanceData | null;\n /** Loading state */\n loading?: boolean;\n /** Error message */\n error?: string | null;\n /** API client for saving corrections */\n apiClient?: BaseApiClient;\n /** Custom labels */\n labels?: Partial<StudentActivityPerformanceLabels>;\n}\n\n/**\n * Configuration for info card variants\n */\ntype InfoVariant = 'time' | 'best' | 'hardest';\n\nconst infoVariantConfig: Record<\n InfoVariant,\n {\n borderColor: string;\n labelColor: string;\n IconComponent: Icon;\n iconColor: string;\n }\n> = {\n time: {\n borderColor: 'border-primary-200',\n labelColor: 'text-primary-600',\n IconComponent: TimerIcon,\n iconColor: 'text-primary-600',\n },\n best: {\n borderColor: 'border-success-200',\n labelColor: 'text-success-600',\n IconComponent: TrophyIcon,\n iconColor: 'text-success-600',\n },\n hardest: {\n borderColor: 'border-error-200',\n labelColor: 'text-error-600',\n IconComponent: WarningIcon,\n iconColor: 'text-error-600',\n },\n};\n\n/**\n * Info card for time/best result/hardest topic\n */\nconst InfoCard = ({\n label,\n value,\n variant,\n}: {\n label: string;\n value: string | null;\n variant: InfoVariant;\n}) => {\n const config = infoVariantConfig[variant];\n\n return (\n <div\n className={cn(\n 'border rounded-xl py-3 px-4 flex flex-col items-center justify-center gap-2',\n config.borderColor\n )}\n >\n <Text className={cn('text-2xs font-medium uppercase', config.labelColor)}>\n {label}\n </Text>\n <Badge size=\"large\" action=\"info\">\n {value || '-'}\n </Badge>\n </div>\n );\n};\n\n/**\n * Determine the status of an alternative based on correctness and selection\n */\nconst getAlternativeStatus = (\n isCorrect: boolean,\n isSelected: boolean\n): OptionStatus | undefined => {\n if (isCorrect) return OptionStatus.CORRECT;\n if (isSelected) return OptionStatus.INCORRECT;\n return undefined;\n};\n\n/**\n * Convert question alternatives to AlternativesList format\n */\nconst mapAlternatives = (question: LessonQuestion): Alternative[] => {\n return question.alternatives.map((alt) => ({\n value: alt.id,\n label: alt.text,\n status: getAlternativeStatus(alt.isCorrect, alt.isSelected),\n }));\n};\n\n/**\n * Get selected alternative value from question\n */\nconst getSelectedValue = (question: LessonQuestion): string | undefined => {\n const selected = question.alternatives.find((alt) => alt.isSelected);\n return selected?.id;\n};\n\n/**\n * Get badge action based on isCorrect value\n */\nconst getBadgeAction = (\n isCorrect: boolean | null\n): 'success' | 'error' | 'info' => {\n if (isCorrect === true) return 'success';\n if (isCorrect === false) return 'error';\n return 'info';\n};\n\n/**\n * Get badge label based on isCorrect value using provided labels\n */\nconst getBadgeLabel = (\n isCorrect: boolean | null,\n labels: StudentActivityPerformanceLabels\n): string => {\n if (isCorrect === true) return labels.markCorrect;\n if (isCorrect === false) return labels.markIncorrect;\n return labels.pending;\n};\n\n/**\n * Generate a unique key for a question\n * Uses answerId if present, otherwise falls back to activityId-questionId composite\n */\nconst getQuestionKey = (question: LessonQuestion): string => {\n if (question.answerId) {\n return question.answerId;\n }\n return `${question.activityId}-${question.id}`;\n};\n\n/**\n * Lesson item with progress bar\n */\nconst LessonItem = ({ lesson }: { lesson: PerformanceLesson }) => (\n <div className=\"bg-background rounded-xl border border-border-50 py-4 px-4 flex flex-col gap-2\">\n <Text size=\"sm\" weight=\"semibold\" className=\"text-text-950\">\n {lesson.title}\n </Text>\n <div className=\"flex items-center gap-2\">\n <ProgressBar\n value={lesson.progress}\n size=\"small\"\n variant=\"blue\"\n className=\"flex-1\"\n />\n <Text\n size=\"xs\"\n className=\"text-primary-700 font-medium min-w-[40px] text-right\"\n >\n {lesson.progress}%\n </Text>\n </div>\n </div>\n);\n\n/**\n * Loading skeleton\n */\nconst LoadingSkeleton = () => (\n <div\n data-testid=\"loading-skeleton\"\n className=\"flex flex-col gap-4 animate-pulse\"\n >\n <div className=\"h-6 bg-background-200 rounded w-48\" />\n <div className=\"grid grid-cols-3 gap-3\">\n <div className=\"h-28 bg-background-200 rounded-xl\" />\n <div className=\"h-28 bg-background-200 rounded-xl\" />\n <div className=\"h-28 bg-background-200 rounded-xl\" />\n </div>\n <div className=\"grid grid-cols-3 gap-3\">\n <div className=\"h-20 bg-background-200 rounded-xl\" />\n <div className=\"h-20 bg-background-200 rounded-xl\" />\n <div className=\"h-20 bg-background-200 rounded-xl\" />\n </div>\n </div>\n);\n\n/**\n * Error content\n */\nconst ErrorContent = ({ message }: { message: string }) => (\n <div className=\"flex flex-col items-center justify-center py-8 gap-3\">\n <Text\n as=\"span\"\n className=\"size-12 rounded-full bg-error-100 flex items-center justify-center\"\n >\n <WarningCircleIcon size={24} className=\"text-error-700\" />\n </Text>\n <Text size=\"md\" className=\"text-error-700 text-center\">\n {message}\n </Text>\n </div>\n);\n\n/**\n * StudentActivityPerformanceModal component\n *\n * Displays a modal with student activity performance including:\n * - Student name\n * - Result cards (score, correct/incorrect counts, time, best/hardest topics)\n * - Activities list with expandable questions and correction controls\n * - Lessons section with progress bars\n */\nexport const StudentActivityPerformanceModal = ({\n isOpen,\n onClose,\n data,\n loading = false,\n error = null,\n apiClient,\n labels: customLabels,\n}: StudentActivityPerformanceModalProps) => {\n const labels = useMemo(\n () => ({ ...DEFAULT_ACTIVITY_PERFORMANCE_LABELS, ...customLabels }),\n [customLabels]\n );\n\n // Toast store for notifications\n const addToast = useToastStore((state) => state.addToast);\n\n // State for essay question corrections (keyed by question id)\n const [essayCorrections, setEssayCorrections] = useState<\n Record<string, EssayCorrectionState>\n >({});\n\n // Initialize essay corrections from data when modal opens\n useEffect(() => {\n if (isOpen && data) {\n const initialCorrections: Record<string, EssayCorrectionState> = {};\n\n data.activities.forEach((activity) => {\n activity.questions.forEach((question) => {\n // Initialize all questions with their current state using unique key\n const questionKey = getQuestionKey(question);\n initialCorrections[questionKey] = {\n isCorrect: question.isCorrect,\n teacherFeedback: question.teacherFeedback || '',\n isSaving: false,\n isSaved: question.isCorrect != null,\n };\n });\n });\n\n setEssayCorrections(initialCorrections);\n }\n }, [isOpen, data]);\n\n /**\n * Update essay correction state\n * Resets isSaved to false when any field changes so badge only updates after saving\n */\n const updateEssayCorrection = useCallback(\n (\n questionId: string,\n field:\n | (typeof EssayCorrectionField)['IsCorrect']\n | (typeof EssayCorrectionField)['TeacherFeedback'],\n value: boolean | string\n ) => {\n setEssayCorrections((prev) => ({\n ...prev,\n [questionId]: {\n ...prev[questionId],\n [field]: value,\n // Reset isSaved when any field changes so badge doesn't update until saved\n isSaved: false,\n },\n }));\n },\n []\n );\n\n /**\n * Handle saving essay question correction\n */\n const handleSaveEssayCorrection = useCallback(\n async (question: LessonQuestion) => {\n if (!apiClient || !data) return;\n\n const questionKey = getQuestionKey(question);\n const correction = essayCorrections[questionKey];\n if (correction?.isCorrect == null) {\n return;\n }\n\n setEssayCorrections((prev) => ({\n ...prev,\n [questionKey]: { ...prev[questionKey], isSaving: true },\n }));\n\n try {\n await apiClient.post(\n `/activities/${question.activityId}/students/${data.userId}/questions/correction`,\n {\n questionId: question.id,\n isCorrect: correction.isCorrect,\n teacherFeedback: correction.teacherFeedback,\n }\n );\n\n // Mark as saved and show success toast\n setEssayCorrections((prev) => ({\n ...prev,\n [questionKey]: {\n ...prev[questionKey],\n isSaving: false,\n isSaved: true,\n },\n }));\n\n addToast({\n title: 'Correção salva',\n description: `A correção da questão foi salva com sucesso.`,\n variant: 'solid',\n action: 'success',\n position: 'top-right',\n });\n } catch (err) {\n console.error('Erro ao salvar correção da questão:', err);\n\n setEssayCorrections((prev) => ({\n ...prev,\n [questionKey]: { ...prev[questionKey], isSaving: false },\n }));\n\n addToast({\n title: 'Erro ao salvar correção',\n description: 'Não foi possível salvar a correção. Tente novamente.',\n variant: 'solid',\n action: 'warning',\n position: 'top-right',\n });\n }\n },\n [apiClient, data, essayCorrections, addToast]\n );\n\n // Reset corrections when modal closes\n const handleClose = useCallback(() => {\n setEssayCorrections({});\n onClose();\n }, [onClose]);\n\n /**\n * Render correction fields for a question (radio group, textarea, save button)\n * Same pattern as CorrectActivityModal\n */\n const renderCorrectionFields = (question: LessonQuestion) => {\n const questionKey = getQuestionKey(question);\n const correction = essayCorrections[questionKey] || {\n isCorrect: null,\n teacherFeedback: '',\n isSaving: false,\n isSaved: false,\n };\n\n let radioValue: string | undefined;\n if (correction.isCorrect === null) {\n radioValue = undefined;\n } else if (correction.isCorrect) {\n radioValue = 'true';\n } else {\n radioValue = 'false';\n }\n\n return (\n <div className=\"space-y-4 border-t border-border-100 pt-4 mt-4\">\n {/* Is correct radio group */}\n <div className=\"space-y-2\">\n <Text className=\"text-sm font-semibold text-text-950\">\n {labels.isCorrectQuestionLabel}\n </Text>\n <div className=\"flex gap-4\">\n <Radio\n name={`isCorrect-${questionKey}`}\n value=\"true\"\n id={`correct-yes-${questionKey}`}\n label={labels.correctYes}\n size=\"medium\"\n checked={radioValue === 'true'}\n disabled={correction.isSaving}\n onChange={(e) => {\n if (e.target.checked) {\n updateEssayCorrection(\n questionKey,\n EssayCorrectionField.IsCorrect,\n true\n );\n }\n }}\n />\n <Radio\n name={`isCorrect-${questionKey}`}\n value=\"false\"\n id={`correct-no-${questionKey}`}\n label={labels.correctNo}\n size=\"medium\"\n checked={radioValue === 'false'}\n disabled={correction.isSaving}\n onChange={(e) => {\n if (e.target.checked) {\n updateEssayCorrection(\n questionKey,\n EssayCorrectionField.IsCorrect,\n false\n );\n }\n }}\n />\n </div>\n </div>\n\n {/* Teacher feedback textarea */}\n <div className=\"space-y-2\">\n <Text className=\"text-sm font-semibold text-text-950\">\n {labels.observationLabel}\n </Text>\n <TextArea\n value={correction.teacherFeedback}\n onChange={(e) => {\n updateEssayCorrection(\n questionKey,\n EssayCorrectionField.TeacherFeedback,\n e.target.value\n );\n }}\n placeholder={labels.feedbackPlaceholder}\n rows={4}\n size=\"medium\"\n disabled={correction.isSaving}\n />\n </div>\n\n {/* Save button */}\n <Button\n size=\"small\"\n onClick={() => handleSaveEssayCorrection(question)}\n disabled={correction.isCorrect === null || correction.isSaving}\n >\n {correction.isSaving ? labels.saving : labels.saveCorrection}\n </Button>\n </div>\n );\n };\n\n /**\n * Render question accordion item inside an activity\n */\n const renderQuestionAccordionItem = (\n question: LessonQuestion,\n index: number\n ) => {\n const questionKey = getQuestionKey(question);\n\n // Check if we have a local correction\n const localCorrection = essayCorrections[questionKey];\n const hasLocalSavedCorrection =\n localCorrection?.isSaved && localCorrection?.isCorrect != null;\n\n // Use local correction status if saved, otherwise use original\n const displayIsCorrect = hasLocalSavedCorrection\n ? localCorrection.isCorrect\n : question.isCorrect;\n\n return (\n <CardAccordation\n key={questionKey}\n value={questionKey}\n className=\"bg-background rounded-xl border border-border-50\"\n triggerClassName=\"py-4 px-4\"\n contentClassName=\"px-4 pb-4\"\n trigger={\n <div className=\"flex items-center justify-between flex-1\">\n <Text size=\"sm\" weight=\"semibold\" className=\"text-text-950\">\n Questão {index + 1}\n </Text>\n <Badge\n size=\"small\"\n action={getBadgeAction(displayIsCorrect)}\n variant=\"solid\"\n >\n {getBadgeLabel(displayIsCorrect, labels)}\n </Badge>\n </div>\n }\n >\n <div className=\"flex flex-col gap-4\">\n {/* Question statement */}\n <HtmlMathRenderer\n content={question.statement}\n className=\"text-sm text-text-700\"\n />\n\n {/* Alternatives (for multiple choice questions) */}\n {question.alternatives.length > 0 && (\n <CardAccordation\n value={`${questionKey}-alternatives`}\n className=\"bg-background rounded-lg border border-border-50\"\n triggerClassName=\"py-4 px-4\"\n contentClassName=\"px-4 py-4\"\n trigger={\n <Text size=\"sm\" weight=\"medium\" className=\"text-text-800\">\n Alternativas\n </Text>\n }\n >\n <AlternativesList\n mode=\"readonly\"\n alternatives={mapAlternatives(question)}\n selectedValue={getSelectedValue(question)}\n layout=\"default\"\n />\n </CardAccordation>\n )}\n\n {/* Correction fields (only for essay questions) */}\n {apiClient &&\n question.questionType === 'DISSERTATIVA' &&\n renderCorrectionFields(question)}\n </div>\n </CardAccordation>\n );\n };\n\n /**\n * Render activity accordion item\n */\n const renderActivityAccordionItem = (activity: PerformanceActivity) => (\n <CardAccordation\n key={activity.id}\n value={activity.id}\n className=\"bg-background rounded-xl border border-border-50\"\n triggerClassName=\"py-4 px-4\"\n contentClassName=\"px-4 pb-4\"\n trigger={\n <Text size=\"sm\" weight=\"semibold\" className=\"text-text-950\">\n {activity.title}\n </Text>\n }\n >\n <AccordionGroup type=\"multiple\" className=\"flex flex-col gap-2\">\n {activity.questions.map((question, index) =>\n renderQuestionAccordionItem(question, index)\n )}\n </AccordionGroup>\n </CardAccordation>\n );\n\n if (!data && !loading && !error) {\n return null;\n }\n\n const renderContent = (): ReactNode => {\n if (loading) {\n return <LoadingSkeleton />;\n }\n\n if (error) {\n return <ErrorContent message={error} />;\n }\n\n if (data) {\n const formattedScore = data.score == null ? '-' : data.score.toFixed(1);\n\n return (\n <div className=\"flex flex-col gap-5\">\n {/* Student name with icon */}\n <div className=\"flex items-center gap-2\">\n <Text\n as=\"span\"\n className=\"size-8 rounded-full bg-background-100 flex items-center justify-center\"\n >\n <UserIcon size={16} className=\"text-text-500\" />\n </Text>\n <Text size=\"md\" weight=\"medium\" className=\"text-text-950\">\n {data.studentName}\n </Text>\n </div>\n\n {/* Result title */}\n <Text size=\"md\" weight=\"semibold\" className=\"text-text-950\">\n {labels.resultTitle}\n </Text>\n\n {/* Top row: Score, Correct, Incorrect */}\n <div className=\"grid grid-cols-3 gap-3\">\n <StatCard\n label={labels.scoreLabel}\n value={formattedScore}\n variant=\"score\"\n />\n <StatCard\n label={labels.correctAnswersLabel}\n value={data.correctAnswers}\n variant=\"correct\"\n />\n <StatCard\n label={labels.incorrectAnswersLabel}\n value={data.incorrectAnswers}\n variant=\"incorrect\"\n />\n </div>\n\n {/* Bottom row: Time, Best Result, Hardest Topic */}\n <div className=\"grid grid-cols-3 gap-3\">\n <InfoCard\n label={labels.completionTimeLabel}\n value={data.completionTime}\n variant=\"time\"\n />\n <InfoCard\n label={labels.bestResultLabel}\n value={data.bestResult}\n variant=\"best\"\n />\n <InfoCard\n label={labels.hardestTopicLabel}\n value={data.hardestTopic}\n variant=\"hardest\"\n />\n </div>\n\n {/* Activities section */}\n {data.activities.length > 0 && (\n <div className=\"flex flex-col gap-3\">\n <Text size=\"md\" weight=\"semibold\" className=\"text-text-950\">\n {labels.activitiesTitle}\n </Text>\n <AccordionGroup type=\"single\" className=\"flex flex-col gap-2\">\n {data.activities.map((activity) =>\n renderActivityAccordionItem(activity)\n )}\n </AccordionGroup>\n </div>\n )}\n\n {/* Lessons section */}\n {data.lessons && data.lessons.length > 0 && (\n <div className=\"flex flex-col gap-3\">\n <Text size=\"md\" weight=\"semibold\" className=\"text-text-950\">\n {labels.lessonsTitle}\n </Text>\n <div className=\"flex flex-col gap-2\">\n {data.lessons.map((lesson) => (\n <LessonItem key={lesson.id} lesson={lesson} />\n ))}\n </div>\n </div>\n )}\n </div>\n );\n }\n\n return null;\n };\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={handleClose}\n title={labels.title}\n size=\"lg\"\n contentClassName=\"max-h-[70vh] overflow-y-auto\"\n >\n {renderContent()}\n </Modal>\n );\n};\n\nexport default StudentActivityPerformanceModal;\n","import { useState, useCallback, useEffect } from 'react';\nimport Modal from '../../Modal/Modal';\nimport Text from '../../Text/Text';\nimport Button from '../../Button/Button';\nimport Badge from '../../Badge/Badge';\nimport Alert from '../../Alert/Alert';\nimport VideoPlayer from '../../VideoPlayer/VideoPlayer';\nimport { CardAudio, CardBase } from '../../Card/Card';\nimport Whiteboard from '../../Whiteboard/Whiteboard';\nimport { CardAccordation } from '../../Accordation';\nimport { Skeleton, SkeletonRounded } from '../../Skeleton/Skeleton';\nimport type {\n LessonDetailsData,\n RecommendedClassLessonsItem,\n} from '../../../types/recommendedLessons';\nimport type { Lesson } from '../../../types/lessons';\nimport type { BaseApiClient } from '../../../types/api';\nimport type { WhiteboardImage } from '../../Whiteboard/Whiteboard';\nimport {\n GenericDisplayStatus,\n getStatusBadgeAction,\n} from '../../../types/common';\nimport { formatDate } from '../utils/lessonDetailsUtils';\nimport { cn, getSubjectColorWithOpacity } from '../../../utils/utils';\nimport { useTheme } from '../../../hooks/useTheme';\n\n/**\n * Props for RecommendedLessonViewModal component\n */\nexport interface RecommendedLessonViewModalProps {\n /** Whether the modal is open */\n isOpen: boolean;\n /** Callback to close the modal */\n onClose: () => void;\n /** Lesson details data */\n data: LessonDetailsData;\n /** API client for fetching full lesson data */\n apiClient?: BaseApiClient;\n}\n\n/**\n * Derive display status from recommended class dates and progress\n */\nconst deriveRecommendedClassStatus = (\n startDate: string | null,\n finalDate: string | null,\n progress: number\n): GenericDisplayStatus => {\n // If progress is 100%, it's completed\n if (progress >= 100) return GenericDisplayStatus.CONCLUIDA;\n\n // Check if deadline has passed\n if (finalDate) {\n const deadline = new Date(finalDate);\n if (deadline < new Date()) {\n return GenericDisplayStatus.VENCIDA;\n }\n }\n\n // Otherwise it's active\n return GenericDisplayStatus.ATIVA;\n};\n\n/**\n * Loading skeleton for lesson content\n */\nconst LessonContentSkeleton = () => (\n <div className=\"flex flex-col gap-4 p-4\">\n <SkeletonRounded height={200} className=\"w-full\" />\n <Skeleton variant=\"text\" width=\"80%\" height={20} />\n <SkeletonRounded height={60} className=\"w-full\" />\n </div>\n);\n\n/**\n * Lesson content component that fetches and displays full lesson data\n */\nconst LessonContent = ({\n lessonId,\n apiClient,\n cachedLessons,\n onLessonLoaded,\n}: {\n lessonId: string;\n apiClient?: BaseApiClient;\n cachedLessons: Record<string, Lesson>;\n onLessonLoaded: (lessonId: string, lesson: Lesson) => void;\n}) => {\n const [loading, setLoading] = useState(!cachedLessons[lessonId]);\n const [error, setError] = useState<string | null>(null);\n\n const lesson = cachedLessons[lessonId];\n\n useEffect(() => {\n if (lesson || !apiClient) return;\n\n const fetchLesson = async () => {\n setLoading(true);\n setError(null);\n try {\n const response = await apiClient.get<{ data: Lesson }>(\n `/lesson/${lessonId}`\n );\n onLessonLoaded(lessonId, response.data.data);\n } catch {\n setError('Erro ao carregar dados da aula');\n } finally {\n setLoading(false);\n }\n };\n\n fetchLesson();\n }, [lessonId, apiClient, lesson, onLessonLoaded]);\n\n if (loading) {\n return <LessonContentSkeleton />;\n }\n\n if (error) {\n return (\n <div className=\"p-4\">\n <Text size=\"sm\" className=\"text-error-700\">\n {error}\n </Text>\n </div>\n );\n }\n\n if (!lesson) {\n return (\n <div className=\"p-4\">\n <Text size=\"sm\" className=\"text-text-500\">\n Dados da aula não disponíveis\n </Text>\n </div>\n );\n }\n\n const hasVideo = Boolean(lesson.urlVideo);\n const hasPodcast = Boolean(lesson.urlPodCast);\n const boardImages: WhiteboardImage[] = [];\n\n if (lesson.urlInitialFrame) {\n boardImages.push({ id: 'initial', imageUrl: lesson.urlInitialFrame });\n }\n if (lesson.urlFinalFrame) {\n boardImages.push({ id: 'final', imageUrl: lesson.urlFinalFrame });\n }\n\n const hasBoards = boardImages.length > 0;\n\n return (\n <div className=\"flex flex-col gap-4\">\n {hasVideo ? (\n <VideoPlayer\n src={lesson.urlVideo!}\n poster={lesson.urlCover}\n subtitles={lesson.urlSubtitle}\n className=\"w-full rounded-lg\"\n />\n ) : (\n <div className=\"p-4 bg-background-50 rounded-lg\">\n <Text size=\"sm\" className=\"text-text-500\">\n Vídeo não disponível para esta aula.\n </Text>\n </div>\n )}\n\n <Alert\n action=\"info\"\n variant=\"solid\"\n description=\"Cada aula inclui questionários automáticos para o aluno praticar o conteúdo.\"\n className=\"w-full\"\n />\n\n {hasPodcast && (\n <div className=\"w-full\">\n <Text size=\"md\" weight=\"bold\" className=\"pb-2\">\n Podcast\n </Text>\n <CardAudio\n src={lesson.urlPodCast!}\n title={lesson.podCastTitle || 'Podcast da aula'}\n />\n </div>\n )}\n\n {hasBoards && (\n <div className=\"w-full\">\n <Text size=\"md\" weight=\"bold\" className=\"pb-2\">\n Quadros da aula\n </Text>\n <div className=\"flex flex-wrap items-center justify-center gap-4\">\n {boardImages.map((image) => (\n <div\n key={image.id}\n className=\"flex flex-row rounded-xl bg-background-50\"\n >\n <Whiteboard\n images={[image]}\n showDownload={true}\n imagesPerRow={2}\n className=\"gap-4 w-full items-center border-border-50\"\n />\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n );\n};\n\n/**\n * Single lesson accordion item with colored header\n */\nconst LessonAccordionItem = ({\n lessonItem,\n apiClient,\n cachedLessons,\n onLessonLoaded,\n}: {\n lessonItem: RecommendedClassLessonsItem;\n apiClient?: BaseApiClient;\n cachedLessons: Record<string, Lesson>;\n onLessonLoaded: (lessonId: string, lesson: Lesson) => void;\n}) => {\n const [isExpanded, setIsExpanded] = useState(false);\n const { isDark } = useTheme();\n const lesson = lessonItem.supLessonsProgress.lesson;\n const lessonId = lesson.id;\n const subjectColor = lesson.subject?.color;\n\n const trigger = (\n <div className=\"flex flex-col gap-1 py-3\">\n <Text size=\"xs\" className=\"text-text-500\">\n {lesson.topic?.name} • {lesson.subtopic?.name} • {lesson.content?.name}\n </Text>\n <Text size=\"md\" weight=\"medium\" className=\"text-text-950\">\n {lesson.content?.name || lesson.videoTitle}\n </Text>\n </div>\n );\n\n return (\n <div className=\"rounded-xl overflow-hidden border border-border-100\">\n {/* Colored header bar */}\n <div\n className={cn(\n 'px-4 py-2',\n subjectColor?.startsWith('bg-') && subjectColor\n )}\n style={\n subjectColor && !subjectColor.startsWith('bg-')\n ? {\n backgroundColor: getSubjectColorWithOpacity(\n subjectColor,\n isDark\n ),\n }\n : undefined\n }\n >\n <Text size=\"xs\" weight=\"medium\" className=\"text-text-700\">\n {lesson.subject?.name}\n </Text>\n </div>\n\n <CardAccordation\n trigger={trigger}\n expanded={isExpanded}\n onToggleExpanded={setIsExpanded}\n value={lessonId}\n contentClassName=\"p-0\"\n className=\"border-0 rounded-none\"\n >\n {isExpanded && (\n <LessonContent\n lessonId={lessonId}\n apiClient={apiClient}\n cachedLessons={cachedLessons}\n onLessonLoaded={onLessonLoaded}\n />\n )}\n </CardAccordation>\n </div>\n );\n};\n\n/**\n * Modal component for viewing all lessons in a recommended class\n * Shows header with metadata and expandable accordion list of lessons\n */\nexport const RecommendedLessonViewModal = ({\n isOpen,\n onClose,\n data,\n apiClient,\n}: RecommendedLessonViewModalProps) => {\n const [cachedLessons, setCachedLessons] = useState<Record<string, Lesson>>(\n {}\n );\n\n const handleLessonLoaded = useCallback((lessonId: string, lesson: Lesson) => {\n setCachedLessons((prev) => ({ ...prev, [lessonId]: lesson }));\n }, []);\n\n // Reset cache when modal closes\n useEffect(() => {\n if (!isOpen) {\n setCachedLessons({});\n }\n }, [isOpen]);\n\n const { recommendedClass } = data;\n const lessons = recommendedClass.lessons || [];\n\n const status = deriveRecommendedClassStatus(\n recommendedClass.startDate,\n recommendedClass.finalDate,\n recommendedClass.progress\n );\n const badgeAction = getStatusBadgeAction(status);\n\n const footer = (\n <div className=\"flex justify-end\">\n <Button variant=\"outline\" onClick={onClose}>\n Fechar\n </Button>\n </div>\n );\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={onClose}\n title=\"Ver Aula\"\n size=\"lg\"\n footer={footer}\n contentClassName=\"max-h-[70vh] overflow-y-auto\"\n >\n <div className=\"flex flex-col gap-6\">\n {/* Header Card */}\n <CardBase layout=\"vertical\" padding=\"medium\" className=\"bg-background\">\n <div className=\"flex flex-col gap-2\">\n <Text as=\"h2\" size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n {recommendedClass.title}\n </Text>\n <div className=\"flex flex-wrap items-center gap-2\">\n <Text size=\"sm\" className=\"text-text-600\">\n Início: {formatDate(recommendedClass.startDate)}\n </Text>\n <Text size=\"sm\" className=\"text-text-400\">\n •\n </Text>\n <Text size=\"sm\" className=\"text-text-600\">\n Prazo: {formatDate(recommendedClass.finalDate)}\n </Text>\n <Text size=\"sm\" className=\"text-text-400\">\n •\n </Text>\n <Badge action={badgeAction} size=\"small\">\n {status}\n </Badge>\n </div>\n </div>\n </CardBase>\n\n {/* Lessons Section */}\n <div className=\"flex flex-col gap-4\">\n <Text as=\"h3\" size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n Aulas\n </Text>\n\n {lessons.length === 0 ? (\n <div className=\"p-4 bg-background-50 rounded-lg\">\n <Text size=\"sm\" className=\"text-text-500\">\n Nenhuma aula encontrada.\n </Text>\n </div>\n ) : (\n <div className=\"flex flex-col gap-3\">\n {lessons.map((lessonItem) => (\n <LessonAccordionItem\n key={lessonItem.supLessonsProgress.lesson.id}\n lessonItem={lessonItem}\n apiClient={apiClient}\n cachedLessons={cachedLessons}\n onLessonLoaded={handleLessonLoaded}\n />\n ))}\n </div>\n )}\n </div>\n </div>\n </Modal>\n );\n};\n\nexport default RecommendedLessonViewModal;\n","import { ANSWER_STATUS } from '../../Quiz/useQuizStore';\nimport type {\n LessonQuestion,\n PerformanceActivity,\n PerformanceLesson,\n StudentActivityPerformanceData,\n} from '../types';\n\n/**\n * Answer data from the student answers API\n */\nexport interface StudentAnswerData {\n id: string;\n questionId: string;\n answer: string | null;\n selectedOptions: Array<{ optionId: string }>;\n answerStatus: string;\n statement: string;\n questionType: string;\n difficultyLevel: string;\n solutionExplanation: string | null;\n correctOption: string | null;\n teacherFeedback: string | null;\n attachment: string | null;\n score: number | null;\n options?: Array<{\n id: string;\n option: string;\n isCorrect: boolean;\n }>;\n knowledgeMatrix: Array<{\n areaKnowledge: { id: string; name: string };\n subject: { id: string; name: string; color: string; icon: string };\n topic: { id: string; name: string };\n subtopic: { id: string; name: string };\n content: { id: string; name: string };\n }>;\n}\n\n/**\n * Activity statistics from the API\n */\nexport interface ActivityStatistics {\n totalAnswered: number;\n correctAnswers: number;\n incorrectAnswers: number;\n pendingAnswers: number;\n score: number | null;\n timeSpent: number;\n}\n\n/**\n * Activity data from the student answers API\n */\nexport interface StudentActivityData {\n id: string;\n title: string;\n sequence: number;\n answers: StudentAnswerData[];\n statistics: ActivityStatistics;\n}\n\n/**\n * Lesson data from the student answers API\n */\nexport interface StudentLessonData {\n id: string;\n title: string;\n sequence: number;\n progress: number;\n completedAt: string | null;\n questionnaire: {\n id: string;\n answers: StudentAnswerData[];\n statistics: ActivityStatistics;\n } | null;\n}\n\n/**\n * Response from /recommended-class/{id}/student/{studentId}/answers\n */\nexport interface StudentAnswersResponse {\n message: string;\n data: {\n activities: StudentActivityData[];\n lessons: StudentLessonData[];\n };\n}\n\n/**\n * Determine if answer is correct based on answer status\n * Returns null for pending/unevaluated answers\n */\nexport const getIsCorrectFromStatus = (\n answerStatus: string\n): boolean | null => {\n if (answerStatus === ANSWER_STATUS.RESPOSTA_CORRETA) return true;\n if (answerStatus === ANSWER_STATUS.RESPOSTA_INCORRETA) return false;\n // For pending, blank, or other statuses, return null (not yet evaluated)\n return null;\n};\n\n/**\n * Convert answer data to LessonQuestion format\n */\nexport const convertAnswerToLessonQuestion = (\n answer: StudentAnswerData,\n index: number,\n activityId: string\n): LessonQuestion => {\n const isCorrect = getIsCorrectFromStatus(answer.answerStatus);\n\n return {\n id: answer.questionId,\n answerId: answer.id,\n activityId,\n title: `Questão ${index + 1}`,\n statement: answer.statement || '',\n questionType: answer.questionType,\n isCorrect,\n teacherFeedback: answer.teacherFeedback,\n alternatives: (answer.options || []).map((opt) => ({\n id: opt.id,\n text: opt.option,\n isCorrect: opt.isCorrect ?? false,\n isSelected:\n answer.selectedOptions?.some((s) => s.optionId === opt.id) ?? false,\n })),\n };\n};\n\n/**\n * Convert activity data to PerformanceActivity format\n */\nexport const convertActivityToPerformance = (\n activity: StudentActivityData\n): PerformanceActivity => {\n const questions: LessonQuestion[] = activity.answers.map((answer, index) =>\n convertAnswerToLessonQuestion(answer, index, activity.id)\n );\n\n return {\n id: activity.id,\n title: activity.title,\n questions,\n };\n};\n\n/**\n * Convert lesson data to PerformanceLesson format\n */\nexport const convertLessonToPerformance = (\n lesson: StudentLessonData\n): PerformanceLesson => {\n return {\n id: lesson.id,\n title: lesson.title,\n progress: lesson.progress,\n };\n};\n\n/**\n * Convert API response to StudentActivityPerformanceData\n */\nexport const convertStudentAnswersToPerformanceData = (\n response: StudentAnswersResponse,\n userInstitutionId: string,\n userId: string,\n studentName: string\n): StudentActivityPerformanceData => {\n const { activities, lessons } = response.data;\n\n // Convert activities\n const performanceActivities = activities.map(convertActivityToPerformance);\n\n // Convert lessons\n const performanceLessons = lessons.map(convertLessonToPerformance);\n\n // Aggregate statistics from all activities\n const totalCorrect = activities.reduce(\n (sum, a) => sum + a.statistics.correctAnswers,\n 0\n );\n const totalIncorrect = activities.reduce(\n (sum, a) => sum + a.statistics.incorrectAnswers,\n 0\n );\n const scores = activities\n .map((a) => a.statistics.score)\n .filter((s): s is number => s != null);\n const avgScore =\n scores.length > 0\n ? scores.reduce((sum, s) => sum + s, 0) / scores.length\n : null;\n\n return {\n userInstitutionId,\n userId,\n studentName,\n score: avgScore,\n correctAnswers: totalCorrect,\n incorrectAnswers: totalIncorrect,\n completionTime: null,\n bestResult: null,\n hardestTopic: null,\n activities: performanceActivities,\n lessons: performanceLessons,\n };\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-TVQJ4WOR.js","../src/components/RecommendedLessonDetails/RecommendedLessonDetails.tsx","../src/components/RecommendedLessonDetails/components/Breadcrumb.tsx","../src/components/RecommendedLessonDetails/components/LessonHeader.tsx","../src/components/RecommendedLessonDetails/utils/lessonDetailsUtils.ts","../src/components/RecommendedLessonDetails/components/LoadingSkeleton.tsx","../src/components/RecommendedLessonDetails/components/ResultsSection.tsx","../src/components/RecommendedLessonDetails/components/StudentsTable.tsx","../src/components/RecommendedLessonDetails/components/StudentPerformanceModal.tsx","../src/components/RecommendedLessonDetails/components/StudentActivityPerformanceModal.tsx","../src/components/RecommendedLessonDetails/components/RecommendedLessonViewModal.tsx","../src/components/RecommendedLessonDetails/utils/performanceUtils.ts"],"names":["jsx","jsxs","useMemo","TrophyIcon","WarningIcon","useEffect","useState","LessonAccordionItem","useCallback"],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-TVQJ4WOR.js","sourcesContent":[null,"import { useMemo, useState, useCallback } from 'react';\nimport Text from '../Text/Text';\nimport useToastStore from '../Toast/utils/ToastStore';\nimport { cn } from '../../utils/utils';\nimport type { LessonDetailsData } from '../../types/recommendedLessons';\nimport type { SubjectEnum } from '../../enums/SubjectEnum';\nimport type { BaseApiClient } from '../../types/api';\nimport {\n Breadcrumb,\n LessonHeader,\n LoadingSkeleton,\n ResultsSection,\n StudentsTable,\n StudentActivityPerformanceModal,\n RecommendedLessonViewModal,\n} from './components';\nimport { transformStudentForDisplay } from './utils/lessonDetailsUtils';\nimport {\n convertStudentAnswersToPerformanceData,\n type StudentAnswersResponse,\n} from './utils/performanceUtils';\nimport {\n DEFAULT_LABELS,\n type BreadcrumbItem,\n type LessonDetailsLabels,\n type StudentActivityPerformanceData,\n} from './types';\n\n/**\n * Props for RecommendedLessonDetails component\n */\nexport interface RecommendedLessonDetailsProps {\n /** Lesson data to display (from API responses) */\n data: LessonDetailsData | null;\n /** Loading state */\n loading?: boolean;\n /** Error message */\n error?: string | null;\n /** Callback when \"Ver aula\" button is clicked */\n onViewLesson?: () => void;\n /**\n * API client for making requests.\n * When provided, enables the \"Corrigir atividade\" functionality.\n */\n apiClient?: BaseApiClient;\n /** Callback for breadcrumb navigation */\n onBreadcrumbClick?: (path: string) => void;\n /** Function to map subject name to SubjectEnum */\n mapSubjectNameToEnum?: (name: string) => SubjectEnum | null;\n /** Custom breadcrumb items */\n breadcrumbs?: BreadcrumbItem[];\n /** Custom labels */\n labels?: Partial<LessonDetailsLabels>;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * RecommendedLessonDetails component\n *\n * Displays detailed information about a recommended lesson including:\n * - Breadcrumb navigation\n * - Lesson metadata (title, dates, school, subject, class)\n * - Results cards (completion %, best result topic, hardest topic)\n * - Students table with status, progress, and actions\n *\n * @example\n * ```tsx\n * <RecommendedLessonDetails\n * data={{\n * recommendedClass: recommendedClassData, // from /recommendedClass/{id}\n * details: detailsData, // from /recommendedClass/{id}/details\n * breakdown: breakdown, // optional, from /recommended-class/history\n * }}\n * apiClient={api}\n * onViewLesson={() => navigate('/view-lesson')}\n * mapSubjectNameToEnum={mapSubjectNameToEnum}\n * />\n * ```\n */\nconst RecommendedLessonDetails = ({\n data,\n loading = false,\n error = null,\n onViewLesson,\n apiClient,\n onBreadcrumbClick,\n mapSubjectNameToEnum,\n breadcrumbs,\n labels: customLabels,\n className,\n}: RecommendedLessonDetailsProps) => {\n const labels = useMemo(\n () => ({ ...DEFAULT_LABELS, ...customLabels }),\n [customLabels]\n );\n\n // Toast store for notifications\n const addToast = useToastStore((state) => state.addToast);\n\n // Activity performance modal state\n const [performanceModalOpen, setPerformanceModalOpen] = useState(false);\n const [performanceData, setPerformanceData] =\n useState<StudentActivityPerformanceData | null>(null);\n const [performanceLoading, setPerformanceLoading] = useState(false);\n const [performanceError, setPerformanceError] = useState<string | null>(null);\n\n // View lesson modal state\n const [viewLessonModalOpen, setViewLessonModalOpen] = useState(false);\n\n /**\n * Handle correct activity action\n * Fetches all activities and lessons for the student and opens the performance modal\n */\n const handleCorrectActivity = useCallback(\n async (studentId: string) => {\n if (!apiClient || !data?.recommendedClass.id) return;\n\n // Find student from data\n const student = data?.details.students.find(\n (s) => s.userInstitutionId === studentId\n );\n\n // Guard: ensure student and userId are valid before proceeding\n if (!student?.userId) {\n addToast({\n title: 'Erro ao carregar aluno',\n description: 'Não foi possível identificar o aluno. Tente novamente.',\n variant: 'solid',\n action: 'warning',\n position: 'top-right',\n });\n return;\n }\n\n const studentName = student.name;\n const userId = student.userId;\n\n setPerformanceModalOpen(true);\n setPerformanceLoading(true);\n setPerformanceData(null);\n setPerformanceError(null);\n\n try {\n // Fetch all answers for the student in this recommended class\n const response = await apiClient.get<StudentAnswersResponse>(\n `/recommended-class/${data.recommendedClass.id}/student/${studentId}/answers`\n );\n\n // Convert API response to performance data format\n const performanceData = convertStudentAnswersToPerformanceData(\n response.data,\n studentId,\n userId,\n studentName\n );\n\n setPerformanceData(performanceData);\n } catch (err) {\n console.error('Error fetching activity performance data:', err);\n setPerformanceError(\n err instanceof Error\n ? err.message\n : 'Erro ao carregar desempenho do aluno'\n );\n } finally {\n setPerformanceLoading(false);\n }\n },\n [apiClient, data?.recommendedClass.id, data?.details.students, addToast]\n );\n\n /**\n * Handle close performance modal\n */\n const handleClosePerformanceModal = useCallback(() => {\n setPerformanceModalOpen(false);\n setPerformanceData(null);\n setPerformanceError(null);\n }, []);\n\n /**\n * Handle view lesson action\n * If onViewLesson prop is provided, call it; otherwise open internal modal\n */\n const handleViewLesson = useCallback(() => {\n if (onViewLesson) {\n onViewLesson();\n } else {\n setViewLessonModalOpen(true);\n }\n }, [onViewLesson]);\n\n /**\n * Handle close view lesson modal\n */\n const handleCloseViewLessonModal = useCallback(() => {\n setViewLessonModalOpen(false);\n }, []);\n\n const defaultBreadcrumbs: BreadcrumbItem[] = useMemo(\n () => [\n { label: 'Aulas recomendadas', path: '/aulas-recomendadas' },\n { label: data?.recommendedClass.title || 'Detalhes' },\n ],\n [data?.recommendedClass.title]\n );\n\n const breadcrumbItems = breadcrumbs || defaultBreadcrumbs;\n\n // Transform API students to display format\n const displayStudents = useMemo(() => {\n if (!data?.details.students) return [];\n const deadline = data?.recommendedClass.finalDate;\n return data.details.students.map((student) =>\n transformStudentForDisplay(student, deadline)\n );\n }, [data?.details.students, data?.recommendedClass.finalDate]);\n\n if (loading) {\n return (\n <div\n className={cn('flex flex-col gap-6', className)}\n data-testid=\"lesson-details-loading\"\n >\n <LoadingSkeleton />\n </div>\n );\n }\n\n if (error) {\n return (\n <div\n className={cn(\n 'flex flex-col items-center justify-center py-12',\n className\n )}\n data-testid=\"lesson-details-error\"\n >\n <Text size=\"md\" className=\"text-error-700\">\n {error}\n </Text>\n </div>\n );\n }\n\n if (!data) {\n return null;\n }\n\n return (\n <>\n <div\n className={cn('flex flex-col gap-6', className)}\n data-testid=\"recommended-class-details\"\n >\n {/* Breadcrumb */}\n <Breadcrumb items={breadcrumbItems} onItemClick={onBreadcrumbClick} />\n\n {/* Header with metadata */}\n <LessonHeader\n data={data}\n onViewLesson={handleViewLesson}\n mapSubjectNameToEnum={mapSubjectNameToEnum}\n viewLessonLabel={labels.viewLesson}\n />\n\n {/* Results section */}\n <ResultsSection data={data} labels={labels} />\n\n {/* Students table */}\n <StudentsTable\n students={displayStudents}\n onCorrectActivity={apiClient ? handleCorrectActivity : undefined}\n labels={labels}\n />\n </div>\n\n {/* Student Activity Performance Modal */}\n {apiClient && (\n <StudentActivityPerformanceModal\n isOpen={performanceModalOpen}\n onClose={handleClosePerformanceModal}\n data={performanceData}\n loading={performanceLoading}\n error={performanceError}\n apiClient={apiClient}\n />\n )}\n\n {/* View Lesson Modal */}\n <RecommendedLessonViewModal\n isOpen={viewLessonModalOpen}\n onClose={handleCloseViewLessonModal}\n data={data}\n apiClient={apiClient}\n />\n </>\n );\n};\n\nexport default RecommendedLessonDetails;\n","import { CaretRightIcon } from '@phosphor-icons/react';\nimport Text from '../../Text/Text';\nimport type { BreadcrumbItem } from '../types';\n\n/**\n * Props for Breadcrumb component\n */\ninterface BreadcrumbProps {\n /** Breadcrumb items to display */\n items: BreadcrumbItem[];\n /** Callback when a breadcrumb item is clicked */\n onItemClick?: (path: string) => void;\n}\n\n/**\n * Breadcrumb navigation component\n * Displays a path of navigation items with optional click handlers\n */\nexport const Breadcrumb = ({ items, onItemClick }: BreadcrumbProps) => (\n <nav className=\"flex items-center gap-2 text-sm\" aria-label=\"Breadcrumb\">\n {items.map((item, index) => (\n <Text\n key={item.path ?? item.label}\n as=\"span\"\n className=\"flex items-center gap-2\"\n >\n {index > 0 && <CaretRightIcon size={14} className=\"text-text-500\" />}\n {item.path ? (\n <button\n type=\"button\"\n onClick={() => onItemClick?.(item.path!)}\n className=\"text-text-600 hover:text-primary-700 transition-colors\"\n >\n {item.label}\n </button>\n ) : (\n <Text as=\"span\" className=\"text-text-950 font-medium\">\n {item.label}\n </Text>\n )}\n </Text>\n ))}\n </nav>\n);\n\nexport default Breadcrumb;\n","import { BookBookmarkIcon } from '@phosphor-icons/react';\nimport Text from '../../Text/Text';\nimport Button from '../../Button/Button';\nimport { getSubjectInfo } from '../../SubjectInfo/SubjectInfo';\nimport { cn } from '../../../utils/utils';\nimport type { LessonDetailsData } from '../../../types/recommendedLessons';\nimport type { SubjectEnum } from '../../../enums/SubjectEnum';\nimport { formatDate } from '../utils/lessonDetailsUtils';\n\n/**\n * Props for LessonHeader component\n */\ninterface LessonHeaderProps {\n /** Lesson details data */\n data: LessonDetailsData;\n /** Callback when \"Ver aula\" button is clicked */\n onViewLesson?: () => void;\n /** Function to map subject name to SubjectEnum */\n mapSubjectNameToEnum?: (name: string) => SubjectEnum | null;\n /** Label for the view lesson button */\n viewLessonLabel: string;\n}\n\n/**\n * Header section with lesson metadata\n * Displays title, dates, school, subject, and class information\n */\nexport const LessonHeader = ({\n data,\n onViewLesson,\n mapSubjectNameToEnum,\n viewLessonLabel,\n}: LessonHeaderProps) => {\n const { recommendedClass, breakdown } = data;\n\n // Extract subject from first lesson if available\n const subjectName =\n recommendedClass.lessons[0]?.supLessonsProgress?.lesson?.subject?.name ||\n '';\n const subjectEnum = mapSubjectNameToEnum?.(subjectName);\n const subjectInfo = subjectEnum ? getSubjectInfo(subjectEnum) : null;\n\n return (\n <div className=\"bg-background rounded-xl border border-border-50 p-6\">\n <div className=\"flex flex-col lg:flex-row lg:items-start lg:justify-between gap-4\">\n <div className=\"flex flex-col gap-2\">\n <Text\n as=\"h1\"\n size=\"xl\"\n weight=\"bold\"\n className=\"text-text-950 lg:text-2xl\"\n >\n {recommendedClass.title}\n </Text>\n <div className=\"flex flex-wrap items-center gap-2 text-sm text-text-600\">\n <Text as=\"span\" size=\"sm\" className=\"text-text-600\">\n Início em {formatDate(recommendedClass.startDate)}\n </Text>\n <Text as=\"span\" size=\"sm\" className=\"text-text-400\">\n •\n </Text>\n <Text as=\"span\" size=\"sm\" className=\"text-text-600\">\n Prazo final {formatDate(recommendedClass.finalDate)}\n </Text>\n {breakdown?.schoolName && (\n <>\n <Text as=\"span\" size=\"sm\" className=\"text-text-400\">\n •\n </Text>\n <Text as=\"span\" size=\"sm\" className=\"text-text-600\">\n {breakdown.schoolName}\n </Text>\n </>\n )}\n {subjectName && (\n <>\n <Text as=\"span\" size=\"sm\" className=\"text-text-400\">\n •\n </Text>\n <Text as=\"span\" size=\"sm\" className=\"flex items-center gap-1\">\n {subjectInfo && (\n <Text\n as=\"span\"\n className={cn(\n 'size-5 rounded flex items-center justify-center text-white',\n subjectInfo.colorClass\n )}\n >\n {subjectInfo.icon}\n </Text>\n )}\n {subjectName}\n </Text>\n </>\n )}\n {breakdown?.className && (\n <>\n <Text as=\"span\" size=\"sm\" className=\"text-text-400\">\n •\n </Text>\n <Text as=\"span\" size=\"sm\" className=\"text-text-600\">\n {breakdown.className}\n </Text>\n </>\n )}\n </div>\n </div>\n {onViewLesson && (\n <Button\n variant=\"solid\"\n action=\"primary\"\n size=\"small\"\n iconLeft={<BookBookmarkIcon size={16} />}\n onClick={onViewLesson}\n >\n {viewLessonLabel}\n </Button>\n )}\n </div>\n </div>\n );\n};\n\nexport default LessonHeader;\n","import type { RecommendedClassDetailStudent } from '../../../types/recommendedLessons';\nimport {\n deriveStudentStatus,\n formatDaysToComplete,\n} from '../../../types/recommendedLessons';\nimport type { DisplayStudent } from '../types';\n\n/**\n * Format date string to display format (DD/MM/YYYY)\n * @param dateString - ISO date string or null\n * @returns Formatted date string or placeholder\n */\nexport const formatDate = (dateString: string | null): string => {\n if (!dateString) return '-';\n const date = new Date(dateString);\n if (Number.isNaN(date.getTime())) return '-';\n return date.toLocaleDateString('pt-BR', {\n day: '2-digit',\n month: '2-digit',\n year: 'numeric',\n });\n};\n\n/**\n * Transform API student data to display format\n * @param student - Student data from API\n * @param deadline - RecommendedClass deadline to determine NAO_FINALIZADO status\n * @returns Transformed student for table display\n */\nexport const transformStudentForDisplay = (\n student: RecommendedClassDetailStudent,\n deadline?: string | null\n): DisplayStudent => ({\n id: student.userInstitutionId,\n name: student.name,\n status: deriveStudentStatus(student.progress, student.completedAt, deadline),\n completionPercentage: student.progress,\n duration: formatDaysToComplete(student.daysToComplete),\n});\n","import {\n SkeletonText,\n SkeletonRounded,\n SkeletonTable,\n} from '../../Skeleton/Skeleton';\n\n/**\n * Loading skeleton component for RecommendedLessonDetails\n * Uses the shared Skeleton components from the library\n */\nexport const LoadingSkeleton = () => (\n <div className=\"flex flex-col gap-6\">\n {/* Breadcrumb skeleton */}\n <SkeletonText width={256} />\n\n {/* Header skeleton */}\n <div className=\"bg-background rounded-xl border border-border-50 p-6\">\n <div className=\"flex flex-col gap-3\">\n <SkeletonText width=\"75%\" height={28} />\n <SkeletonText width=\"50%\" />\n </div>\n </div>\n\n {/* Results section skeleton */}\n <div className=\"flex flex-col gap-4\">\n <SkeletonText width={192} height={20} />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <SkeletonRounded height={140} />\n <SkeletonRounded height={140} />\n <SkeletonRounded height={140} />\n </div>\n </div>\n\n {/* Table skeleton */}\n <div className=\"bg-background rounded-xl border border-border-50 p-4\">\n <SkeletonTable rows={4} columns={5} />\n </div>\n </div>\n);\n\nexport default LoadingSkeleton;\n","import { TrophyIcon, WarningIcon } from '@phosphor-icons/react';\nimport Text from '../../Text/Text';\nimport ProgressCircle from '../../ProgressCircle/ProgressCircle';\nimport type { LessonDetailsData } from '../../../types/recommendedLessons';\nimport type { LessonDetailsLabels } from '../types';\n\n/**\n * Props for ResultsSection component\n */\ninterface ResultsSectionProps {\n /** Lesson details data */\n data: LessonDetailsData;\n /** Labels for the component */\n labels: LessonDetailsLabels;\n}\n\n/**\n * Results section with 3 cards\n * Displays completion percentage, best result, and hardest topic\n */\nexport const ResultsSection = ({ data, labels }: ResultsSectionProps) => {\n const { details } = data;\n const { aggregated, contentPerformance } = details;\n\n return (\n <div className=\"flex flex-col gap-4\">\n <Text as=\"h2\" size=\"md\" weight=\"semibold\" className=\"text-text-950\">\n {labels.resultsTitle}\n </Text>\n {/* White container with the 3 cards */}\n <div className=\"bg-background rounded-xl border border-border-50 p-4\">\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n {/* Completion percentage card */}\n <div className=\"flex flex-col items-center justify-center rounded-xl p-4 min-h-28 bg-primary-50\">\n <ProgressCircle\n value={aggregated.completionPercentage}\n size=\"small\"\n variant=\"blue\"\n label={labels.completedLabel}\n showPercentage\n />\n </div>\n\n {/* Best result topic card */}\n <div className=\"flex flex-col items-center justify-center rounded-xl p-4 min-h-28 bg-success-200\">\n <Text\n as=\"span\"\n className=\"size-8 rounded-full flex items-center justify-center bg-warning-300 mb-2\"\n >\n <TrophyIcon size={18} weight=\"fill\" className=\"text-white\" />\n </Text>\n <Text\n size=\"2xs\"\n weight=\"bold\"\n className=\"text-text-700 uppercase text-center leading-none mb-1\"\n >\n {labels.bestResultLabel}\n </Text>\n <Text\n size=\"xl\"\n weight=\"bold\"\n className=\"text-success-700 text-center leading-none tracking-wide\"\n >\n {contentPerformance.best?.contentName || '-'}\n </Text>\n </div>\n\n {/* Hardest topic card */}\n <div className=\"flex flex-col items-center justify-center rounded-xl p-4 min-h-28 bg-error-100\">\n <Text\n as=\"span\"\n className=\"size-8 rounded-full flex items-center justify-center bg-error-300 mb-2\"\n >\n <WarningIcon size={18} weight=\"fill\" className=\"text-error-700\" />\n </Text>\n <Text\n size=\"2xs\"\n weight=\"bold\"\n className=\"text-text-700 uppercase text-center leading-none mb-1\"\n >\n {labels.hardestTopicLabel}\n </Text>\n <Text\n size=\"xl\"\n weight=\"bold\"\n className=\"text-error-700 text-center leading-none tracking-wide\"\n >\n {contentPerformance.worst?.contentName || '-'}\n </Text>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default ResultsSection;\n","import { UserIcon } from '@phosphor-icons/react';\nimport Text from '../../Text/Text';\nimport { TruncatedText } from '../../TruncatedText/TruncatedText';\nimport Button from '../../Button/Button';\nimport Badge from '../../Badge/Badge';\nimport ProgressBar from '../../ProgressBar/ProgressBar';\nimport Table, {\n TableHeader,\n TableBody,\n TableHead,\n TableRow,\n TableCell,\n useTableSort,\n} from '../../Table/Table';\nimport { getStudentStatusBadgeAction } from '../../../types/recommendedLessons';\nimport type { DisplayStudent, LessonDetailsLabels } from '../types';\n\n/**\n * Props for StudentsTable component\n */\ninterface StudentsTableProps {\n /** List of students to display */\n students: DisplayStudent[];\n /** Callback when correct activity is clicked */\n onCorrectActivity?: (studentId: string) => void;\n /** Labels for the component */\n labels: LessonDetailsLabels;\n /** Empty state message */\n emptyMessage?: string;\n}\n\n/**\n * Students table component\n * Displays students with their status, completion, and duration\n */\nexport const StudentsTable = ({\n students,\n onCorrectActivity,\n labels,\n emptyMessage = 'Nenhum aluno encontrado',\n}: StudentsTableProps) => {\n const { sortedData, sortColumn, sortDirection, handleSort } =\n useTableSort<DisplayStudent>(students);\n\n return (\n <div className=\"bg-background rounded-xl border border-border-50 overflow-hidden\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead\n sortable\n sortDirection={sortColumn === 'name' ? sortDirection : undefined}\n onSort={() => handleSort('name')}\n >\n {labels.studentColumn}\n </TableHead>\n <TableHead\n sortable\n sortDirection={\n sortColumn === 'status' ? sortDirection : undefined\n }\n onSort={() => handleSort('status')}\n >\n {labels.statusColumn}\n </TableHead>\n <TableHead\n sortable\n sortDirection={\n sortColumn === 'completionPercentage'\n ? sortDirection\n : undefined\n }\n onSort={() => handleSort('completionPercentage')}\n >\n {labels.completionColumn}\n </TableHead>\n <TableHead>{labels.durationColumn}</TableHead>\n <TableHead className=\"w-[160px]\" />\n </TableRow>\n </TableHeader>\n <TableBody>\n {sortedData.length === 0 ? (\n <TableRow>\n <TableCell colSpan={5} className=\"text-center py-8\">\n <Text size=\"sm\" className=\"text-text-500\">\n {emptyMessage}\n </Text>\n </TableCell>\n </TableRow>\n ) : (\n sortedData.map((student) => (\n <TableRow key={student.id}>\n <TableCell className=\"max-w-[220px]\">\n <div className=\"flex items-center gap-2 min-w-0\">\n <Text\n as=\"span\"\n className=\"size-8 rounded-full bg-background-100 flex items-center justify-center shrink-0\"\n >\n <UserIcon size={16} className=\"text-text-500\" />\n </Text>\n <TruncatedText size=\"sm\" color=\"text-text-950\">\n {student.name}\n </TruncatedText>\n </div>\n </TableCell>\n <TableCell>\n <Badge\n variant=\"solid\"\n action={getStudentStatusBadgeAction(student.status)}\n size=\"small\"\n >\n {student.status}\n </Badge>\n </TableCell>\n <TableCell>\n <div className=\"flex flex-col gap-1 min-w-[120px]\">\n <Text size=\"sm\" className=\"text-primary-700 font-medium\">\n {student.completionPercentage}%\n </Text>\n <ProgressBar\n value={student.completionPercentage}\n size=\"small\"\n variant=\"blue\"\n className=\"w-full max-w-[100px]\"\n />\n </div>\n </TableCell>\n <TableCell>\n <Text size=\"sm\" className=\"text-text-700\">\n {student.duration ?? '-'}\n </Text>\n </TableCell>\n <TableCell>\n {onCorrectActivity && (\n <Button\n variant=\"outline\"\n size=\"extra-small\"\n onClick={() => onCorrectActivity(student.id)}\n >\n {labels.correctActivity}\n </Button>\n )}\n </TableCell>\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </div>\n );\n};\n\nexport default StudentsTable;\n","import { useMemo, type ReactNode } from 'react';\nimport { LightbulbFilamentIcon } from '@phosphor-icons/react/dist/csr/LightbulbFilament';\nimport { WarningCircleIcon } from '@phosphor-icons/react/dist/csr/WarningCircle';\nimport { UserIcon } from '@phosphor-icons/react/dist/csr/User';\nimport { CheckCircleIcon } from '@phosphor-icons/react/dist/csr/CheckCircle';\nimport { XCircleIcon } from '@phosphor-icons/react/dist/csr/XCircle';\nimport Modal from '../../Modal/Modal';\nimport Text from '../../Text/Text';\nimport Badge from '../../Badge/Badge';\nimport ProgressBar from '../../ProgressBar/ProgressBar';\nimport { CardAccordation } from '../../Accordation/Accordation';\nimport {\n AlternativesList,\n type Alternative,\n} from '../../Alternative/Alternative';\nimport { OptionStatus } from '../../../enums/Options';\nimport type {\n StudentPerformanceData,\n StudentPerformanceLabels,\n LessonProgress,\n LessonQuestion,\n} from '../types';\nimport { DEFAULT_PERFORMANCE_LABELS } from '../types';\nimport { HtmlMathRenderer } from '../../HtmlMathRenderer';\n\n/**\n * Props for StudentPerformanceModal component\n */\nexport interface StudentPerformanceModalProps {\n /** Whether the modal is open */\n isOpen: boolean;\n /** Callback to close the modal */\n onClose: () => void;\n /** Student performance data */\n data: StudentPerformanceData | null;\n /** Loading state */\n loading?: boolean;\n /** Error message */\n error?: string | null;\n /** Custom labels */\n labels?: Partial<StudentPerformanceLabels>;\n}\n\n/**\n * Performance card with colored header and white footer section\n * Matches the Figma design with icon, label, value on top and secondary info below\n */\nconst PerformanceCard = ({\n icon,\n label,\n value,\n secondaryLabel,\n secondaryValue,\n variant,\n}: {\n icon: ReactNode;\n label: string;\n value: string | number;\n secondaryLabel: string;\n secondaryValue: string | null;\n variant: 'success' | 'error';\n}) => {\n const headerBgColor = {\n success: 'bg-success-200',\n error: 'bg-error-100',\n }[variant];\n\n const valueColor = {\n success: 'text-success-700',\n error: 'text-error-700',\n }[variant];\n\n const iconBgColor = {\n success: 'bg-warning-300',\n error: 'bg-error-300',\n }[variant];\n\n const secondaryLabelColor = {\n success: 'text-success-600',\n error: 'text-error-600',\n }[variant];\n\n return (\n <div className=\"flex flex-col rounded-xl border border-border-50 bg-background overflow-hidden\">\n {/* Header section with colored background */}\n <div\n className={`flex flex-col items-center justify-center p-4 gap-1 ${headerBgColor}`}\n >\n <Text\n as=\"span\"\n className={`size-8 rounded-full flex items-center justify-center ${iconBgColor}`}\n >\n {icon}\n </Text>\n <Text\n size=\"2xs\"\n weight=\"medium\"\n className=\"text-text-800 uppercase text-center leading-none\"\n >\n {label}\n </Text>\n <Text size=\"xl\" weight=\"bold\" className={`${valueColor} text-center`}>\n {value}\n </Text>\n </div>\n\n {/* Footer section with white background */}\n <div className=\"flex flex-col items-center gap-2 px-4 py-3\">\n <Text\n size=\"2xs\"\n weight=\"medium\"\n className={`${secondaryLabelColor} uppercase text-center`}\n >\n {secondaryLabel}\n </Text>\n <Badge size=\"large\" action=\"info\">\n {secondaryValue || '-'}\n </Badge>\n </div>\n </div>\n );\n};\n\n/**\n * Determine the status of an alternative based on correctness and selection\n */\nconst getAlternativeStatus = (\n isCorrect: boolean,\n isSelected: boolean\n): OptionStatus | undefined => {\n if (isCorrect) {\n return OptionStatus.CORRECT;\n }\n if (isSelected) {\n return OptionStatus.INCORRECT;\n }\n return undefined;\n};\n\n/**\n * Convert question alternatives to AlternativesList format\n */\nconst mapAlternatives = (question: LessonQuestion): Alternative[] => {\n return question.alternatives.map((alt) => ({\n value: alt.id,\n label: alt.text,\n status: getAlternativeStatus(alt.isCorrect, alt.isSelected),\n }));\n};\n\n/**\n * Get selected alternative value from question\n */\nconst getSelectedValue = (question: LessonQuestion): string | undefined => {\n const selected = question.alternatives.find((alt) => alt.isSelected);\n return selected?.id;\n};\n\n/**\n * Question accordion item inside a lesson\n */\nconst QuestionAccordionItem = ({\n question,\n index,\n}: {\n question: LessonQuestion;\n index: number;\n}) => (\n <CardAccordation\n value={question.id}\n className=\"bg-background rounded-xl border border-border-50\"\n triggerClassName=\"py-5 px-5\"\n contentClassName=\"px-5 pb-5\"\n trigger={\n <div className=\"flex items-center justify-between flex-1\">\n <Text size=\"sm\" weight=\"semibold\" className=\"text-text-950\">\n Questão {index + 1}\n </Text>\n <Badge\n size=\"small\"\n action={question.isCorrect ? 'success' : 'error'}\n variant=\"solid\"\n iconLeft={question.isCorrect ? <CheckCircleIcon /> : <XCircleIcon />}\n >\n {question.isCorrect ? 'Correta' : 'Incorreta'}\n </Badge>\n </div>\n }\n >\n <div className=\"flex flex-col gap-3\">\n {/* Question statement */}\n <HtmlMathRenderer\n content={question.statement}\n className=\"text-sm text-text-700\"\n />\n\n {/* Alternatives accordion */}\n <CardAccordation\n value={`${question.id}-alternatives`}\n className=\"bg-background rounded-lg border border-border-50\"\n triggerClassName=\"py-5 px-5\"\n contentClassName=\"px-5 py-5\"\n trigger={\n <Text size=\"sm\" weight=\"medium\" className=\"text-text-800\">\n Alternativas\n </Text>\n }\n >\n <AlternativesList\n mode=\"readonly\"\n alternatives={mapAlternatives(question)}\n selectedValue={getSelectedValue(question)}\n layout=\"default\"\n />\n </CardAccordation>\n </div>\n </CardAccordation>\n);\n\n/**\n * Lesson accordion item\n */\nconst LessonAccordionItem = ({ lesson }: { lesson: LessonProgress }) => (\n <CardAccordation\n value={lesson.id}\n className=\"bg-background rounded-xl border border-border-50\"\n triggerClassName=\"py-5 px-5\"\n contentClassName=\"px-5 pb-5\"\n trigger={\n <div className=\"flex flex-col gap-1 flex-1\">\n <Text size=\"sm\" weight=\"semibold\" className=\"text-text-950\">\n {lesson.title}\n </Text>\n <ProgressBar\n value={lesson.progress}\n variant=\"blue\"\n size=\"medium\"\n showPercentage\n layout=\"default\"\n />\n </div>\n }\n >\n <div className=\"flex flex-col gap-2\">\n {lesson.questions.map((question, index) => (\n <QuestionAccordionItem\n key={question.id}\n question={question}\n index={index}\n />\n ))}\n </div>\n </CardAccordation>\n);\n\n/**\n * StudentPerformanceModal component\n *\n * Displays a modal with student performance details including:\n * - Student name with search icon\n * - Two performance cards (correct answers, incorrect answers)\n * - Expandable list of lessons with progress\n *\n * @example\n * ```tsx\n * <StudentPerformanceModal\n * isOpen={isModalOpen}\n * onClose={() => setIsModalOpen(false)}\n * data={{\n * studentName: 'Lucas Oliveira',\n * correctAnswers: 8,\n * incorrectAnswers: 7,\n * bestResult: 'Fotossíntese',\n * hardestTopic: 'Células',\n * lessons: [\n * { id: '1', title: 'Categorias Taxonômicas', progress: 50 },\n * ],\n * }}\n * />\n * ```\n */\n/**\n * Loading skeleton for the modal content\n */\nconst LoadingSkeleton = () => (\n <div className=\"flex flex-col gap-4 animate-pulse\">\n <div className=\"h-6 bg-background-200 rounded w-48\" />\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"h-44 bg-background-200 rounded-xl\" />\n <div className=\"h-44 bg-background-200 rounded-xl\" />\n </div>\n </div>\n);\n\n/**\n * Error state component\n */\nconst ErrorContent = ({ message }: { message: string }) => (\n <div className=\"flex flex-col items-center justify-center py-8 gap-3\">\n <Text\n as=\"span\"\n className=\"size-12 rounded-full bg-error-100 flex items-center justify-center\"\n >\n <WarningCircleIcon size={24} className=\"text-error-700\" />\n </Text>\n <Text size=\"md\" className=\"text-error-700 text-center\">\n {message}\n </Text>\n </div>\n);\n\n/**\n * Performance content when data is available\n */\nconst PerformanceContent = ({\n data,\n labels,\n}: {\n data: StudentPerformanceData;\n labels: StudentPerformanceLabels;\n}) => (\n <div className=\"flex flex-col gap-5\">\n {/* Student name with icon */}\n <div className=\"flex items-center gap-2\">\n <Text\n as=\"span\"\n className=\"size-8 rounded-full bg-background-100 flex items-center justify-center\"\n >\n <UserIcon size={16} className=\"text-text-500\" />\n </Text>\n <Text size=\"md\" weight=\"medium\" className=\"text-text-950\">\n {data.studentName}\n </Text>\n </div>\n\n {/* Two performance cards in a row */}\n <div className=\"grid grid-cols-2 gap-3\">\n {/* Correct answers card */}\n <PerformanceCard\n icon={\n <LightbulbFilamentIcon\n size={18}\n weight=\"fill\"\n className=\"text-white\"\n />\n }\n label={labels.correctAnswersLabel}\n value={data.correctAnswers}\n secondaryLabel={labels.bestResultLabel}\n secondaryValue={data.bestResult}\n variant=\"success\"\n />\n\n {/* Incorrect answers card */}\n <PerformanceCard\n icon={\n <WarningCircleIcon\n size={18}\n weight=\"fill\"\n className=\"text-error-700\"\n />\n }\n label={labels.incorrectAnswersLabel}\n value={data.incorrectAnswers}\n secondaryLabel={labels.hardestTopicLabel}\n secondaryValue={data.hardestTopic}\n variant=\"error\"\n />\n </div>\n\n {/* Lessons section */}\n {data.lessons.length > 0 && (\n <div className=\"flex flex-col gap-3\">\n <Text size=\"md\" weight=\"semibold\" className=\"text-text-950\">\n {labels.lessonsTitle}\n </Text>\n <div className=\"flex flex-col gap-2\">\n {data.lessons.map((lesson) => (\n <LessonAccordionItem key={lesson.id} lesson={lesson} />\n ))}\n </div>\n </div>\n )}\n </div>\n);\n\n/**\n * Renders the modal content based on loading, error, and data state\n */\nconst renderModalContent = (\n loading: boolean,\n error: string | null | undefined,\n data: StudentPerformanceData | null,\n labels: StudentPerformanceLabels\n): ReactNode => {\n if (loading) {\n return <LoadingSkeleton />;\n }\n\n if (error) {\n return <ErrorContent message={error} />;\n }\n\n if (data) {\n return <PerformanceContent data={data} labels={labels} />;\n }\n\n return null;\n};\n\nexport const StudentPerformanceModal = ({\n isOpen,\n onClose,\n data,\n loading = false,\n error = null,\n labels: customLabels,\n}: StudentPerformanceModalProps) => {\n const labels = useMemo(\n () => ({ ...DEFAULT_PERFORMANCE_LABELS, ...customLabels }),\n [customLabels]\n );\n\n if (!data && !loading && !error) {\n return null;\n }\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={onClose}\n title={labels.title}\n size=\"lg\"\n contentClassName=\"max-h-[70vh] overflow-y-auto\"\n >\n {renderModalContent(loading, error, data, labels)}\n </Modal>\n );\n};\n\nexport default StudentPerformanceModal;\n","import {\n useMemo,\n useState,\n useCallback,\n useEffect,\n type ReactNode,\n} from 'react';\nimport { TimerIcon } from '@phosphor-icons/react/dist/csr/Timer';\nimport { TrophyIcon } from '@phosphor-icons/react/dist/csr/Trophy';\nimport { WarningIcon } from '@phosphor-icons/react/dist/csr/Warning';\nimport { UserIcon } from '@phosphor-icons/react/dist/csr/User';\nimport { WarningCircleIcon } from '@phosphor-icons/react/dist/csr/WarningCircle';\nimport type { Icon } from '@phosphor-icons/react';\nimport Modal from '../../Modal/Modal';\nimport Text from '../../Text/Text';\nimport Badge from '../../Badge/Badge';\nimport Button from '../../Button/Button';\nimport Radio from '../../Radio/Radio';\nimport TextArea from '../../TextArea/TextArea';\nimport ProgressBar from '../../ProgressBar/ProgressBar';\nimport { CardAccordation, AccordionGroup } from '../../Accordation';\nimport {\n AlternativesList,\n type Alternative,\n} from '../../Alternative/Alternative';\nimport { OptionStatus } from '../../../enums/Options';\nimport useToastStore from '../../Toast/utils/ToastStore';\nimport { StatCard } from '../../shared/StatCard';\nimport type { BaseApiClient } from '../../../types/api';\nimport type {\n StudentActivityPerformanceData,\n StudentActivityPerformanceLabels,\n PerformanceActivity,\n PerformanceLesson,\n LessonQuestion,\n} from '../types';\nimport { DEFAULT_ACTIVITY_PERFORMANCE_LABELS } from '../types';\nimport { cn } from '../../../utils/utils';\nimport { HtmlMathRenderer } from '../../HtmlMathRenderer';\n\n/** Field names for essay correction state updates */\nconst EssayCorrectionField = {\n IsCorrect: 'isCorrect',\n TeacherFeedback: 'teacherFeedback',\n} as const;\n\n/**\n * Essay correction state for a question\n */\ninterface EssayCorrectionState {\n isCorrect: boolean | null;\n teacherFeedback: string;\n isSaving: boolean;\n isSaved: boolean;\n}\n\n/**\n * Props for StudentActivityPerformanceModal component\n */\nexport interface StudentActivityPerformanceModalProps {\n /** Whether the modal is open */\n isOpen: boolean;\n /** Callback to close the modal */\n onClose: () => void;\n /** Student activity performance data */\n data: StudentActivityPerformanceData | null;\n /** Loading state */\n loading?: boolean;\n /** Error message */\n error?: string | null;\n /** API client for saving corrections */\n apiClient?: BaseApiClient;\n /** Custom labels */\n labels?: Partial<StudentActivityPerformanceLabels>;\n}\n\n/**\n * Configuration for info card variants\n */\ntype InfoVariant = 'time' | 'best' | 'hardest';\n\nconst infoVariantConfig: Record<\n InfoVariant,\n {\n borderColor: string;\n labelColor: string;\n IconComponent: Icon;\n iconColor: string;\n }\n> = {\n time: {\n borderColor: 'border-primary-200',\n labelColor: 'text-primary-600',\n IconComponent: TimerIcon,\n iconColor: 'text-primary-600',\n },\n best: {\n borderColor: 'border-success-200',\n labelColor: 'text-success-600',\n IconComponent: TrophyIcon,\n iconColor: 'text-success-600',\n },\n hardest: {\n borderColor: 'border-error-200',\n labelColor: 'text-error-600',\n IconComponent: WarningIcon,\n iconColor: 'text-error-600',\n },\n};\n\n/**\n * Info card for time/best result/hardest topic\n */\nconst InfoCard = ({\n label,\n value,\n variant,\n}: {\n label: string;\n value: string | null;\n variant: InfoVariant;\n}) => {\n const config = infoVariantConfig[variant];\n\n return (\n <div\n className={cn(\n 'border rounded-xl py-3 px-4 flex flex-col items-center justify-center gap-2',\n config.borderColor\n )}\n >\n <Text className={cn('text-2xs font-medium uppercase', config.labelColor)}>\n {label}\n </Text>\n <Badge size=\"large\" action=\"info\">\n {value || '-'}\n </Badge>\n </div>\n );\n};\n\n/**\n * Determine the status of an alternative based on correctness and selection\n */\nconst getAlternativeStatus = (\n isCorrect: boolean,\n isSelected: boolean\n): OptionStatus | undefined => {\n if (isCorrect) return OptionStatus.CORRECT;\n if (isSelected) return OptionStatus.INCORRECT;\n return undefined;\n};\n\n/**\n * Convert question alternatives to AlternativesList format\n */\nconst mapAlternatives = (question: LessonQuestion): Alternative[] => {\n return question.alternatives.map((alt) => ({\n value: alt.id,\n label: alt.text,\n status: getAlternativeStatus(alt.isCorrect, alt.isSelected),\n }));\n};\n\n/**\n * Get selected alternative value from question\n */\nconst getSelectedValue = (question: LessonQuestion): string | undefined => {\n const selected = question.alternatives.find((alt) => alt.isSelected);\n return selected?.id;\n};\n\n/**\n * Get badge action based on isCorrect value\n */\nconst getBadgeAction = (\n isCorrect: boolean | null\n): 'success' | 'error' | 'info' => {\n if (isCorrect === true) return 'success';\n if (isCorrect === false) return 'error';\n return 'info';\n};\n\n/**\n * Get badge label based on isCorrect value using provided labels\n */\nconst getBadgeLabel = (\n isCorrect: boolean | null,\n labels: StudentActivityPerformanceLabels\n): string => {\n if (isCorrect === true) return labels.markCorrect;\n if (isCorrect === false) return labels.markIncorrect;\n return labels.pending;\n};\n\n/**\n * Generate a unique key for a question\n * Uses answerId if present, otherwise falls back to activityId-questionId composite\n */\nconst getQuestionKey = (question: LessonQuestion): string => {\n if (question.answerId) {\n return question.answerId;\n }\n return `${question.activityId}-${question.id}`;\n};\n\n/**\n * Lesson item with progress bar\n */\nconst LessonItem = ({ lesson }: { lesson: PerformanceLesson }) => (\n <div className=\"bg-background rounded-xl border border-border-50 py-4 px-4 flex flex-col gap-2\">\n <Text size=\"sm\" weight=\"semibold\" className=\"text-text-950\">\n {lesson.title}\n </Text>\n <div className=\"flex items-center gap-2\">\n <ProgressBar\n value={lesson.progress}\n size=\"small\"\n variant=\"blue\"\n className=\"flex-1\"\n />\n <Text\n size=\"xs\"\n className=\"text-primary-700 font-medium min-w-[40px] text-right\"\n >\n {lesson.progress}%\n </Text>\n </div>\n </div>\n);\n\n/**\n * Loading skeleton\n */\nconst LoadingSkeleton = () => (\n <div\n data-testid=\"loading-skeleton\"\n className=\"flex flex-col gap-4 animate-pulse\"\n >\n <div className=\"h-6 bg-background-200 rounded w-48\" />\n <div className=\"grid grid-cols-3 gap-3\">\n <div className=\"h-28 bg-background-200 rounded-xl\" />\n <div className=\"h-28 bg-background-200 rounded-xl\" />\n <div className=\"h-28 bg-background-200 rounded-xl\" />\n </div>\n <div className=\"grid grid-cols-3 gap-3\">\n <div className=\"h-20 bg-background-200 rounded-xl\" />\n <div className=\"h-20 bg-background-200 rounded-xl\" />\n <div className=\"h-20 bg-background-200 rounded-xl\" />\n </div>\n </div>\n);\n\n/**\n * Error content\n */\nconst ErrorContent = ({ message }: { message: string }) => (\n <div className=\"flex flex-col items-center justify-center py-8 gap-3\">\n <Text\n as=\"span\"\n className=\"size-12 rounded-full bg-error-100 flex items-center justify-center\"\n >\n <WarningCircleIcon size={24} className=\"text-error-700\" />\n </Text>\n <Text size=\"md\" className=\"text-error-700 text-center\">\n {message}\n </Text>\n </div>\n);\n\n/**\n * StudentActivityPerformanceModal component\n *\n * Displays a modal with student activity performance including:\n * - Student name\n * - Result cards (score, correct/incorrect counts, time, best/hardest topics)\n * - Activities list with expandable questions and correction controls\n * - Lessons section with progress bars\n */\nexport const StudentActivityPerformanceModal = ({\n isOpen,\n onClose,\n data,\n loading = false,\n error = null,\n apiClient,\n labels: customLabels,\n}: StudentActivityPerformanceModalProps) => {\n const labels = useMemo(\n () => ({ ...DEFAULT_ACTIVITY_PERFORMANCE_LABELS, ...customLabels }),\n [customLabels]\n );\n\n // Toast store for notifications\n const addToast = useToastStore((state) => state.addToast);\n\n // State for essay question corrections (keyed by question id)\n const [essayCorrections, setEssayCorrections] = useState<\n Record<string, EssayCorrectionState>\n >({});\n\n // Initialize essay corrections from data when modal opens\n useEffect(() => {\n if (isOpen && data) {\n const initialCorrections: Record<string, EssayCorrectionState> = {};\n\n data.activities.forEach((activity) => {\n activity.questions.forEach((question) => {\n // Initialize all questions with their current state using unique key\n const questionKey = getQuestionKey(question);\n initialCorrections[questionKey] = {\n isCorrect: question.isCorrect,\n teacherFeedback: question.teacherFeedback || '',\n isSaving: false,\n isSaved: question.isCorrect != null,\n };\n });\n });\n\n setEssayCorrections(initialCorrections);\n }\n }, [isOpen, data]);\n\n /**\n * Update essay correction state\n * Resets isSaved to false when any field changes so badge only updates after saving\n */\n const updateEssayCorrection = useCallback(\n (\n questionId: string,\n field:\n | (typeof EssayCorrectionField)['IsCorrect']\n | (typeof EssayCorrectionField)['TeacherFeedback'],\n value: boolean | string\n ) => {\n setEssayCorrections((prev) => ({\n ...prev,\n [questionId]: {\n ...prev[questionId],\n [field]: value,\n // Reset isSaved when any field changes so badge doesn't update until saved\n isSaved: false,\n },\n }));\n },\n []\n );\n\n /**\n * Handle saving essay question correction\n */\n const handleSaveEssayCorrection = useCallback(\n async (question: LessonQuestion) => {\n if (!apiClient || !data) return;\n\n const questionKey = getQuestionKey(question);\n const correction = essayCorrections[questionKey];\n if (correction?.isCorrect == null) {\n return;\n }\n\n setEssayCorrections((prev) => ({\n ...prev,\n [questionKey]: { ...prev[questionKey], isSaving: true },\n }));\n\n try {\n await apiClient.post(\n `/activities/${question.activityId}/students/${data.userId}/questions/correction`,\n {\n questionId: question.id,\n isCorrect: correction.isCorrect,\n teacherFeedback: correction.teacherFeedback,\n }\n );\n\n // Mark as saved and show success toast\n setEssayCorrections((prev) => ({\n ...prev,\n [questionKey]: {\n ...prev[questionKey],\n isSaving: false,\n isSaved: true,\n },\n }));\n\n addToast({\n title: 'Correção salva',\n description: `A correção da questão foi salva com sucesso.`,\n variant: 'solid',\n action: 'success',\n position: 'top-right',\n });\n } catch (err) {\n console.error('Erro ao salvar correção da questão:', err);\n\n setEssayCorrections((prev) => ({\n ...prev,\n [questionKey]: { ...prev[questionKey], isSaving: false },\n }));\n\n addToast({\n title: 'Erro ao salvar correção',\n description: 'Não foi possível salvar a correção. Tente novamente.',\n variant: 'solid',\n action: 'warning',\n position: 'top-right',\n });\n }\n },\n [apiClient, data, essayCorrections, addToast]\n );\n\n // Reset corrections when modal closes\n const handleClose = useCallback(() => {\n setEssayCorrections({});\n onClose();\n }, [onClose]);\n\n /**\n * Render correction fields for a question (radio group, textarea, save button)\n * Same pattern as CorrectActivityModal\n */\n const renderCorrectionFields = (question: LessonQuestion) => {\n const questionKey = getQuestionKey(question);\n const correction = essayCorrections[questionKey] || {\n isCorrect: null,\n teacherFeedback: '',\n isSaving: false,\n isSaved: false,\n };\n\n let radioValue: string | undefined;\n if (correction.isCorrect === null) {\n radioValue = undefined;\n } else if (correction.isCorrect) {\n radioValue = 'true';\n } else {\n radioValue = 'false';\n }\n\n return (\n <div className=\"space-y-4 border-t border-border-100 pt-4 mt-4\">\n {/* Is correct radio group */}\n <div className=\"space-y-2\">\n <Text className=\"text-sm font-semibold text-text-950\">\n {labels.isCorrectQuestionLabel}\n </Text>\n <div className=\"flex gap-4\">\n <Radio\n name={`isCorrect-${questionKey}`}\n value=\"true\"\n id={`correct-yes-${questionKey}`}\n label={labels.correctYes}\n size=\"medium\"\n checked={radioValue === 'true'}\n disabled={correction.isSaving}\n onChange={(e) => {\n if (e.target.checked) {\n updateEssayCorrection(\n questionKey,\n EssayCorrectionField.IsCorrect,\n true\n );\n }\n }}\n />\n <Radio\n name={`isCorrect-${questionKey}`}\n value=\"false\"\n id={`correct-no-${questionKey}`}\n label={labels.correctNo}\n size=\"medium\"\n checked={radioValue === 'false'}\n disabled={correction.isSaving}\n onChange={(e) => {\n if (e.target.checked) {\n updateEssayCorrection(\n questionKey,\n EssayCorrectionField.IsCorrect,\n false\n );\n }\n }}\n />\n </div>\n </div>\n\n {/* Teacher feedback textarea */}\n <div className=\"space-y-2\">\n <Text className=\"text-sm font-semibold text-text-950\">\n {labels.observationLabel}\n </Text>\n <TextArea\n value={correction.teacherFeedback}\n onChange={(e) => {\n updateEssayCorrection(\n questionKey,\n EssayCorrectionField.TeacherFeedback,\n e.target.value\n );\n }}\n placeholder={labels.feedbackPlaceholder}\n rows={4}\n size=\"medium\"\n disabled={correction.isSaving}\n />\n </div>\n\n {/* Save button */}\n <Button\n size=\"small\"\n onClick={() => handleSaveEssayCorrection(question)}\n disabled={correction.isCorrect === null || correction.isSaving}\n >\n {correction.isSaving ? labels.saving : labels.saveCorrection}\n </Button>\n </div>\n );\n };\n\n /**\n * Render question accordion item inside an activity\n */\n const renderQuestionAccordionItem = (\n question: LessonQuestion,\n index: number\n ) => {\n const questionKey = getQuestionKey(question);\n\n // Check if we have a local correction\n const localCorrection = essayCorrections[questionKey];\n const hasLocalSavedCorrection =\n localCorrection?.isSaved && localCorrection?.isCorrect != null;\n\n // Use local correction status if saved, otherwise use original\n const displayIsCorrect = hasLocalSavedCorrection\n ? localCorrection.isCorrect\n : question.isCorrect;\n\n return (\n <CardAccordation\n key={questionKey}\n value={questionKey}\n className=\"bg-background rounded-xl border border-border-50\"\n triggerClassName=\"py-4 px-4\"\n contentClassName=\"px-4 pb-4\"\n trigger={\n <div className=\"flex items-center justify-between flex-1\">\n <Text size=\"sm\" weight=\"semibold\" className=\"text-text-950\">\n Questão {index + 1}\n </Text>\n <Badge\n size=\"small\"\n action={getBadgeAction(displayIsCorrect)}\n variant=\"solid\"\n >\n {getBadgeLabel(displayIsCorrect, labels)}\n </Badge>\n </div>\n }\n >\n <div className=\"flex flex-col gap-4\">\n {/* Question statement */}\n <HtmlMathRenderer\n content={question.statement}\n className=\"text-sm text-text-700\"\n />\n\n {/* Alternatives (for multiple choice questions) */}\n {question.alternatives.length > 0 && (\n <CardAccordation\n value={`${questionKey}-alternatives`}\n className=\"bg-background rounded-lg border border-border-50\"\n triggerClassName=\"py-4 px-4\"\n contentClassName=\"px-4 py-4\"\n trigger={\n <Text size=\"sm\" weight=\"medium\" className=\"text-text-800\">\n Alternativas\n </Text>\n }\n >\n <AlternativesList\n mode=\"readonly\"\n alternatives={mapAlternatives(question)}\n selectedValue={getSelectedValue(question)}\n layout=\"default\"\n />\n </CardAccordation>\n )}\n\n {/* Correction fields (only for essay questions) */}\n {apiClient &&\n question.questionType === 'DISSERTATIVA' &&\n renderCorrectionFields(question)}\n </div>\n </CardAccordation>\n );\n };\n\n /**\n * Render activity accordion item\n */\n const renderActivityAccordionItem = (activity: PerformanceActivity) => (\n <CardAccordation\n key={activity.id}\n value={activity.id}\n className=\"bg-background rounded-xl border border-border-50\"\n triggerClassName=\"py-4 px-4\"\n contentClassName=\"px-4 pb-4\"\n trigger={\n <Text size=\"sm\" weight=\"semibold\" className=\"text-text-950\">\n {activity.title}\n </Text>\n }\n >\n <AccordionGroup type=\"multiple\" className=\"flex flex-col gap-2\">\n {activity.questions.map((question, index) =>\n renderQuestionAccordionItem(question, index)\n )}\n </AccordionGroup>\n </CardAccordation>\n );\n\n if (!data && !loading && !error) {\n return null;\n }\n\n const renderContent = (): ReactNode => {\n if (loading) {\n return <LoadingSkeleton />;\n }\n\n if (error) {\n return <ErrorContent message={error} />;\n }\n\n if (data) {\n const formattedScore = data.score == null ? '-' : data.score.toFixed(1);\n\n return (\n <div className=\"flex flex-col gap-5\">\n {/* Student name with icon */}\n <div className=\"flex items-center gap-2\">\n <Text\n as=\"span\"\n className=\"size-8 rounded-full bg-background-100 flex items-center justify-center\"\n >\n <UserIcon size={16} className=\"text-text-500\" />\n </Text>\n <Text size=\"md\" weight=\"medium\" className=\"text-text-950\">\n {data.studentName}\n </Text>\n </div>\n\n {/* Result title */}\n <Text size=\"md\" weight=\"semibold\" className=\"text-text-950\">\n {labels.resultTitle}\n </Text>\n\n {/* Top row: Score, Correct, Incorrect */}\n <div className=\"grid grid-cols-3 gap-3\">\n <StatCard\n label={labels.scoreLabel}\n value={formattedScore}\n variant=\"score\"\n />\n <StatCard\n label={labels.correctAnswersLabel}\n value={data.correctAnswers}\n variant=\"correct\"\n />\n <StatCard\n label={labels.incorrectAnswersLabel}\n value={data.incorrectAnswers}\n variant=\"incorrect\"\n />\n </div>\n\n {/* Bottom row: Time, Best Result, Hardest Topic */}\n <div className=\"grid grid-cols-3 gap-3\">\n <InfoCard\n label={labels.completionTimeLabel}\n value={data.completionTime}\n variant=\"time\"\n />\n <InfoCard\n label={labels.bestResultLabel}\n value={data.bestResult}\n variant=\"best\"\n />\n <InfoCard\n label={labels.hardestTopicLabel}\n value={data.hardestTopic}\n variant=\"hardest\"\n />\n </div>\n\n {/* Activities section */}\n {data.activities.length > 0 && (\n <div className=\"flex flex-col gap-3\">\n <Text size=\"md\" weight=\"semibold\" className=\"text-text-950\">\n {labels.activitiesTitle}\n </Text>\n <AccordionGroup type=\"single\" className=\"flex flex-col gap-2\">\n {data.activities.map((activity) =>\n renderActivityAccordionItem(activity)\n )}\n </AccordionGroup>\n </div>\n )}\n\n {/* Lessons section */}\n {data.lessons && data.lessons.length > 0 && (\n <div className=\"flex flex-col gap-3\">\n <Text size=\"md\" weight=\"semibold\" className=\"text-text-950\">\n {labels.lessonsTitle}\n </Text>\n <div className=\"flex flex-col gap-2\">\n {data.lessons.map((lesson) => (\n <LessonItem key={lesson.id} lesson={lesson} />\n ))}\n </div>\n </div>\n )}\n </div>\n );\n }\n\n return null;\n };\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={handleClose}\n title={labels.title}\n size=\"lg\"\n contentClassName=\"max-h-[70vh] overflow-y-auto\"\n >\n {renderContent()}\n </Modal>\n );\n};\n\nexport default StudentActivityPerformanceModal;\n","import { useState, useCallback, useEffect } from 'react';\nimport Modal from '../../Modal/Modal';\nimport Text from '../../Text/Text';\nimport Button from '../../Button/Button';\nimport Badge from '../../Badge/Badge';\nimport Alert from '../../Alert/Alert';\nimport VideoPlayer from '../../VideoPlayer/VideoPlayer';\nimport { CardAudio, CardBase } from '../../Card/Card';\nimport Whiteboard from '../../Whiteboard/Whiteboard';\nimport { CardAccordation } from '../../Accordation';\nimport { Skeleton, SkeletonRounded } from '../../Skeleton/Skeleton';\nimport type {\n LessonDetailsData,\n RecommendedClassLessonsItem,\n} from '../../../types/recommendedLessons';\nimport type { Lesson } from '../../../types/lessons';\nimport type { BaseApiClient } from '../../../types/api';\nimport type { WhiteboardImage } from '../../Whiteboard/Whiteboard';\nimport {\n GenericDisplayStatus,\n getStatusBadgeAction,\n} from '../../../types/common';\nimport { formatDate } from '../utils/lessonDetailsUtils';\nimport { cn, getSubjectColorWithOpacity } from '../../../utils/utils';\nimport { useTheme } from '../../../hooks/useTheme';\n\n/**\n * Props for RecommendedLessonViewModal component\n */\nexport interface RecommendedLessonViewModalProps {\n /** Whether the modal is open */\n isOpen: boolean;\n /** Callback to close the modal */\n onClose: () => void;\n /** Lesson details data */\n data: LessonDetailsData;\n /** API client for fetching full lesson data */\n apiClient?: BaseApiClient;\n}\n\n/**\n * Derive display status from recommended class dates and progress\n */\nconst deriveRecommendedClassStatus = (\n startDate: string | null,\n finalDate: string | null,\n progress: number\n): GenericDisplayStatus => {\n // If progress is 100%, it's completed\n if (progress >= 100) return GenericDisplayStatus.CONCLUIDA;\n\n // Check if deadline has passed\n if (finalDate) {\n const deadline = new Date(finalDate);\n if (deadline < new Date()) {\n return GenericDisplayStatus.VENCIDA;\n }\n }\n\n // Otherwise it's active\n return GenericDisplayStatus.ATIVA;\n};\n\n/**\n * Loading skeleton for lesson content\n */\nconst LessonContentSkeleton = () => (\n <div className=\"flex flex-col gap-4 p-4\">\n <SkeletonRounded height={200} className=\"w-full\" />\n <Skeleton variant=\"text\" width=\"80%\" height={20} />\n <SkeletonRounded height={60} className=\"w-full\" />\n </div>\n);\n\n/**\n * Lesson content component that fetches and displays full lesson data\n */\nconst LessonContent = ({\n lessonId,\n apiClient,\n cachedLessons,\n onLessonLoaded,\n}: {\n lessonId: string;\n apiClient?: BaseApiClient;\n cachedLessons: Record<string, Lesson>;\n onLessonLoaded: (lessonId: string, lesson: Lesson) => void;\n}) => {\n const [loading, setLoading] = useState(!cachedLessons[lessonId]);\n const [error, setError] = useState<string | null>(null);\n\n const lesson = cachedLessons[lessonId];\n\n useEffect(() => {\n if (lesson || !apiClient) return;\n\n const fetchLesson = async () => {\n setLoading(true);\n setError(null);\n try {\n const response = await apiClient.get<{ data: Lesson }>(\n `/lesson/${lessonId}`\n );\n onLessonLoaded(lessonId, response.data.data);\n } catch {\n setError('Erro ao carregar dados da aula');\n } finally {\n setLoading(false);\n }\n };\n\n fetchLesson();\n }, [lessonId, apiClient, lesson, onLessonLoaded]);\n\n if (loading) {\n return <LessonContentSkeleton />;\n }\n\n if (error) {\n return (\n <div className=\"p-4\">\n <Text size=\"sm\" className=\"text-error-700\">\n {error}\n </Text>\n </div>\n );\n }\n\n if (!lesson) {\n return (\n <div className=\"p-4\">\n <Text size=\"sm\" className=\"text-text-500\">\n Dados da aula não disponíveis\n </Text>\n </div>\n );\n }\n\n const hasVideo = Boolean(lesson.urlVideo);\n const hasPodcast = Boolean(lesson.urlPodCast);\n const boardImages: WhiteboardImage[] = [];\n\n if (lesson.urlInitialFrame) {\n boardImages.push({ id: 'initial', imageUrl: lesson.urlInitialFrame });\n }\n if (lesson.urlFinalFrame) {\n boardImages.push({ id: 'final', imageUrl: lesson.urlFinalFrame });\n }\n\n const hasBoards = boardImages.length > 0;\n\n return (\n <div className=\"flex flex-col gap-4\">\n {hasVideo ? (\n <VideoPlayer\n src={lesson.urlVideo!}\n poster={lesson.urlCover}\n subtitles={lesson.urlSubtitle}\n className=\"w-full rounded-lg\"\n />\n ) : (\n <div className=\"p-4 bg-background-50 rounded-lg\">\n <Text size=\"sm\" className=\"text-text-500\">\n Vídeo não disponível para esta aula.\n </Text>\n </div>\n )}\n\n <Alert\n action=\"info\"\n variant=\"solid\"\n description=\"Cada aula inclui questionários automáticos para o aluno praticar o conteúdo.\"\n className=\"w-full\"\n />\n\n {hasPodcast && (\n <div className=\"w-full\">\n <Text size=\"md\" weight=\"bold\" className=\"pb-2\">\n Podcast\n </Text>\n <CardAudio\n src={lesson.urlPodCast!}\n title={lesson.podCastTitle || 'Podcast da aula'}\n />\n </div>\n )}\n\n {hasBoards && (\n <div className=\"w-full\">\n <Text size=\"md\" weight=\"bold\" className=\"pb-2\">\n Quadros da aula\n </Text>\n <div className=\"flex flex-wrap items-center justify-center gap-4\">\n {boardImages.map((image) => (\n <div\n key={image.id}\n className=\"flex flex-row rounded-xl bg-background-50\"\n >\n <Whiteboard\n images={[image]}\n showDownload={true}\n imagesPerRow={2}\n className=\"gap-4 w-full items-center border-border-50\"\n />\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n );\n};\n\n/**\n * Single lesson accordion item with colored header\n */\nconst LessonAccordionItem = ({\n lessonItem,\n apiClient,\n cachedLessons,\n onLessonLoaded,\n}: {\n lessonItem: RecommendedClassLessonsItem;\n apiClient?: BaseApiClient;\n cachedLessons: Record<string, Lesson>;\n onLessonLoaded: (lessonId: string, lesson: Lesson) => void;\n}) => {\n const [isExpanded, setIsExpanded] = useState(false);\n const { isDark } = useTheme();\n const lesson = lessonItem.supLessonsProgress.lesson;\n const lessonId = lesson.id;\n const subjectColor = lesson.subject?.color;\n\n const trigger = (\n <div className=\"flex flex-col gap-1 py-3\">\n <Text size=\"xs\" className=\"text-text-500\">\n {lesson.topic?.name} • {lesson.subtopic?.name} • {lesson.content?.name}\n </Text>\n <Text size=\"md\" weight=\"medium\" className=\"text-text-950\">\n {lesson.content?.name || lesson.videoTitle}\n </Text>\n </div>\n );\n\n return (\n <div className=\"rounded-xl overflow-hidden border border-border-100\">\n {/* Colored header bar */}\n <div\n className={cn(\n 'px-4 py-2',\n subjectColor?.startsWith('bg-') && subjectColor\n )}\n style={\n subjectColor && !subjectColor.startsWith('bg-')\n ? {\n backgroundColor: getSubjectColorWithOpacity(\n subjectColor,\n isDark\n ),\n }\n : undefined\n }\n >\n <Text size=\"xs\" weight=\"medium\" className=\"text-text-700\">\n {lesson.subject?.name}\n </Text>\n </div>\n\n <CardAccordation\n trigger={trigger}\n expanded={isExpanded}\n onToggleExpanded={setIsExpanded}\n value={lessonId}\n contentClassName=\"p-0\"\n className=\"border-0 rounded-none\"\n >\n {isExpanded && (\n <LessonContent\n lessonId={lessonId}\n apiClient={apiClient}\n cachedLessons={cachedLessons}\n onLessonLoaded={onLessonLoaded}\n />\n )}\n </CardAccordation>\n </div>\n );\n};\n\n/**\n * Modal component for viewing all lessons in a recommended class\n * Shows header with metadata and expandable accordion list of lessons\n */\nexport const RecommendedLessonViewModal = ({\n isOpen,\n onClose,\n data,\n apiClient,\n}: RecommendedLessonViewModalProps) => {\n const [cachedLessons, setCachedLessons] = useState<Record<string, Lesson>>(\n {}\n );\n\n const handleLessonLoaded = useCallback((lessonId: string, lesson: Lesson) => {\n setCachedLessons((prev) => ({ ...prev, [lessonId]: lesson }));\n }, []);\n\n // Reset cache when modal closes\n useEffect(() => {\n if (!isOpen) {\n setCachedLessons({});\n }\n }, [isOpen]);\n\n const { recommendedClass } = data;\n const lessons = recommendedClass.lessons || [];\n\n const status = deriveRecommendedClassStatus(\n recommendedClass.startDate,\n recommendedClass.finalDate,\n recommendedClass.progress\n );\n const badgeAction = getStatusBadgeAction(status);\n\n const footer = (\n <div className=\"flex justify-end\">\n <Button variant=\"outline\" onClick={onClose}>\n Fechar\n </Button>\n </div>\n );\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={onClose}\n title=\"Ver Aula\"\n size=\"lg\"\n footer={footer}\n contentClassName=\"max-h-[70vh] overflow-y-auto\"\n >\n <div className=\"flex flex-col gap-6\">\n {/* Header Card */}\n <CardBase layout=\"vertical\" padding=\"medium\" className=\"bg-background\">\n <div className=\"flex flex-col gap-2\">\n <Text as=\"h2\" size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n {recommendedClass.title}\n </Text>\n <div className=\"flex flex-wrap items-center gap-2\">\n <Text size=\"sm\" className=\"text-text-600\">\n Início: {formatDate(recommendedClass.startDate)}\n </Text>\n <Text size=\"sm\" className=\"text-text-400\">\n •\n </Text>\n <Text size=\"sm\" className=\"text-text-600\">\n Prazo: {formatDate(recommendedClass.finalDate)}\n </Text>\n <Text size=\"sm\" className=\"text-text-400\">\n •\n </Text>\n <Badge action={badgeAction} size=\"small\">\n {status}\n </Badge>\n </div>\n </div>\n </CardBase>\n\n {/* Lessons Section */}\n <div className=\"flex flex-col gap-4\">\n <Text as=\"h3\" size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n Aulas\n </Text>\n\n {lessons.length === 0 ? (\n <div className=\"p-4 bg-background-50 rounded-lg\">\n <Text size=\"sm\" className=\"text-text-500\">\n Nenhuma aula encontrada.\n </Text>\n </div>\n ) : (\n <div className=\"flex flex-col gap-3\">\n {lessons.map((lessonItem) => (\n <LessonAccordionItem\n key={lessonItem.supLessonsProgress.lesson.id}\n lessonItem={lessonItem}\n apiClient={apiClient}\n cachedLessons={cachedLessons}\n onLessonLoaded={handleLessonLoaded}\n />\n ))}\n </div>\n )}\n </div>\n </div>\n </Modal>\n );\n};\n\nexport default RecommendedLessonViewModal;\n","import { ANSWER_STATUS } from '../../Quiz/useQuizStore';\nimport type {\n LessonQuestion,\n PerformanceActivity,\n PerformanceLesson,\n StudentActivityPerformanceData,\n} from '../types';\n\n/**\n * Answer data from the student answers API\n */\nexport interface StudentAnswerData {\n id: string;\n questionId: string;\n answer: string | null;\n selectedOptions: Array<{ optionId: string }>;\n answerStatus: string;\n statement: string;\n questionType: string;\n difficultyLevel: string;\n solutionExplanation: string | null;\n correctOption: string | null;\n teacherFeedback: string | null;\n attachment: string | null;\n score: number | null;\n options?: Array<{\n id: string;\n option: string;\n isCorrect: boolean;\n }>;\n knowledgeMatrix: Array<{\n areaKnowledge: { id: string; name: string };\n subject: { id: string; name: string; color: string; icon: string };\n topic: { id: string; name: string };\n subtopic: { id: string; name: string };\n content: { id: string; name: string };\n }>;\n}\n\n/**\n * Activity statistics from the API\n */\nexport interface ActivityStatistics {\n totalAnswered: number;\n correctAnswers: number;\n incorrectAnswers: number;\n pendingAnswers: number;\n score: number | null;\n timeSpent: number;\n}\n\n/**\n * Activity data from the student answers API\n */\nexport interface StudentActivityData {\n id: string;\n title: string;\n sequence: number;\n answers: StudentAnswerData[];\n statistics: ActivityStatistics;\n}\n\n/**\n * Lesson data from the student answers API\n */\nexport interface StudentLessonData {\n id: string;\n title: string;\n sequence: number;\n progress: number;\n completedAt: string | null;\n questionnaire: {\n id: string;\n answers: StudentAnswerData[];\n statistics: ActivityStatistics;\n } | null;\n}\n\n/**\n * Response from /recommended-class/{id}/student/{studentId}/answers\n */\nexport interface StudentAnswersResponse {\n message: string;\n data: {\n activities: StudentActivityData[];\n lessons: StudentLessonData[];\n };\n}\n\n/**\n * Determine if answer is correct based on answer status\n * Returns null for pending/unevaluated answers\n */\nexport const getIsCorrectFromStatus = (\n answerStatus: string\n): boolean | null => {\n if (answerStatus === ANSWER_STATUS.RESPOSTA_CORRETA) return true;\n if (answerStatus === ANSWER_STATUS.RESPOSTA_INCORRETA) return false;\n // For pending, blank, or other statuses, return null (not yet evaluated)\n return null;\n};\n\n/**\n * Convert answer data to LessonQuestion format\n */\nexport const convertAnswerToLessonQuestion = (\n answer: StudentAnswerData,\n index: number,\n activityId: string\n): LessonQuestion => {\n const isCorrect = getIsCorrectFromStatus(answer.answerStatus);\n\n return {\n id: answer.questionId,\n answerId: answer.id,\n activityId,\n title: `Questão ${index + 1}`,\n statement: answer.statement || '',\n questionType: answer.questionType,\n isCorrect,\n teacherFeedback: answer.teacherFeedback,\n alternatives: (answer.options || []).map((opt) => ({\n id: opt.id,\n text: opt.option,\n isCorrect: opt.isCorrect ?? false,\n isSelected:\n answer.selectedOptions?.some((s) => s.optionId === opt.id) ?? false,\n })),\n };\n};\n\n/**\n * Convert activity data to PerformanceActivity format\n */\nexport const convertActivityToPerformance = (\n activity: StudentActivityData\n): PerformanceActivity => {\n const questions: LessonQuestion[] = activity.answers.map((answer, index) =>\n convertAnswerToLessonQuestion(answer, index, activity.id)\n );\n\n return {\n id: activity.id,\n title: activity.title,\n questions,\n };\n};\n\n/**\n * Convert lesson data to PerformanceLesson format\n */\nexport const convertLessonToPerformance = (\n lesson: StudentLessonData\n): PerformanceLesson => {\n return {\n id: lesson.id,\n title: lesson.title,\n progress: lesson.progress,\n };\n};\n\n/**\n * Convert API response to StudentActivityPerformanceData\n */\nexport const convertStudentAnswersToPerformanceData = (\n response: StudentAnswersResponse,\n userInstitutionId: string,\n userId: string,\n studentName: string\n): StudentActivityPerformanceData => {\n const { activities, lessons } = response.data;\n\n // Convert activities\n const performanceActivities = activities.map(convertActivityToPerformance);\n\n // Convert lessons\n const performanceLessons = lessons.map(convertLessonToPerformance);\n\n // Aggregate statistics from all activities\n const totalCorrect = activities.reduce(\n (sum, a) => sum + a.statistics.correctAnswers,\n 0\n );\n const totalIncorrect = activities.reduce(\n (sum, a) => sum + a.statistics.incorrectAnswers,\n 0\n );\n const scores = activities\n .map((a) => a.statistics.score)\n .filter((s): s is number => s != null);\n const avgScore =\n scores.length > 0\n ? scores.reduce((sum, s) => sum + s, 0) / scores.length\n : null;\n\n return {\n userInstitutionId,\n userId,\n studentName,\n score: avgScore,\n correctAnswers: totalCorrect,\n incorrectAnswers: totalIncorrect,\n completionTime: null,\n bestResult: null,\n hardestTopic: null,\n activities: performanceActivities,\n lessons: performanceLessons,\n };\n};\n"]}
|
|
@@ -138,7 +138,7 @@ import {
|
|
|
138
138
|
} from "./chunk-W5SKDW2D.mjs";
|
|
139
139
|
import {
|
|
140
140
|
VideoPlayer_default
|
|
141
|
-
} from "./chunk-
|
|
141
|
+
} from "./chunk-INSEDDUA.mjs";
|
|
142
142
|
import {
|
|
143
143
|
useMobile
|
|
144
144
|
} from "./chunk-GKY77WVA.mjs";
|
|
@@ -19907,4 +19907,4 @@ export {
|
|
|
19907
19907
|
ExamDetailsPage,
|
|
19908
19908
|
ExamDetailsPage_default
|
|
19909
19909
|
};
|
|
19910
|
-
//# sourceMappingURL=chunk-
|
|
19910
|
+
//# sourceMappingURL=chunk-ZXQ6CGGV.mjs.map
|