analytica-frontend-lib
Version:
Repositório público dos componentes utilizados nas plataformas da Analytica Ensino
1 lines • 29.4 kB
Source Map (JSON)
{"version":3,"sources":["../src/components/TimeChart/TimeChart.tsx"],"sourcesContent":["import { useState, type HTMLAttributes, type ReactNode } from 'react';\nimport Text from '../Text/Text';\nimport { Tooltip } from '../Tooltip/Tooltip';\nimport { cn } from '../../utils/utils';\nimport {\n bgClassToCssVar,\n polarToCartesian,\n describeArc,\n} from '../../utils/chartUtils';\n\nexport { bgClassToCssVar } from '../../utils/chartUtils';\nimport { PROFILE_ROLES } from '../../types/chat';\nimport type { StudentsHighlightPeriod } from '../../hooks/useStudentsHighlight';\n\n/**\n * Keys for TimeChart categories, matching the API field names.\n */\nexport enum TIME_CHART_CATEGORY_KEY {\n ACTIVITIES = 'activities',\n CONTENT = 'content',\n SIMULATIONS = 'simulations',\n QUESTIONNAIRES = 'questionnaires',\n RECOMMENDED_LESSONS = 'recommendedLessons',\n}\n\n/**\n * A single category of time data (e.g., \"Atividades\", \"Conteúdo\")\n */\nexport interface TimeChartCategory {\n /** Unique key matching the API field name */\n key: TIME_CHART_CATEGORY_KEY;\n /** Display label for legends */\n label: string;\n /** Tailwind bg- color class (e.g., \"bg-success-700\") */\n colorClass: string;\n}\n\n/**\n * A single period entry from the API hoursByPeriod array.\n * Can be passed directly from the API response.\n * The `label` field is the period label, other fields are hours by category key.\n */\nexport interface TimeChartDayData {\n label: string;\n [key: string]: string | number;\n}\n\n/**\n * Complete data for the TimeChart component\n */\nexport interface TimeChartData {\n /** Category definitions (determines colors and legend) */\n categories: TimeChartCategory[];\n /** Per-period breakdown for the stacked bar chart (API hoursByPeriod) */\n hoursByPeriod: TimeChartDayData[];\n /** Percentages for pie chart by category key (API hoursByItem, 0-100).\n * When provided, pie chart uses these. Otherwise computes from hoursByPeriod totals. */\n hoursByItem?: Record<string, number>;\n}\n\n/**\n * Props for the TimeChart component\n */\nexport interface TimeChartProps extends HTMLAttributes<HTMLDivElement> {\n /** Chart data including categories and period breakdown */\n data: TimeChartData;\n /** Title for the bar chart card */\n barChartTitle?: string;\n /** Title for the pie chart card */\n pieChartTitle?: string;\n /** Height of the bar chart area in pixels */\n chartHeight?: number;\n /** SVG pie chart diameter in pixels */\n pieSize?: number;\n /**\n * Suffix appended to the bar chart values (Y-axis and tooltips).\n * Defaults to 'h' (hours). Pass '' for plain counts.\n */\n unitSuffix?: string;\n}\n\n/**\n * Predefined category configs for student profile\n */\nexport const STUDENT_CATEGORIES: TimeChartCategory[] = [\n {\n key: TIME_CHART_CATEGORY_KEY.ACTIVITIES,\n label: 'Atividades',\n colorClass: 'bg-success-700',\n },\n {\n key: TIME_CHART_CATEGORY_KEY.SIMULATIONS,\n label: 'Simulados',\n colorClass: 'bg-warning-300',\n },\n {\n key: TIME_CHART_CATEGORY_KEY.QUESTIONNAIRES,\n label: 'Questionários',\n colorClass: 'bg-indicator-info',\n },\n {\n key: TIME_CHART_CATEGORY_KEY.CONTENT,\n label: 'Videoaulas',\n colorClass: 'bg-success-300',\n },\n {\n key: TIME_CHART_CATEGORY_KEY.RECOMMENDED_LESSONS,\n label: 'Aulas recomendadas',\n colorClass: 'bg-indicator-positive',\n },\n];\n\n/**\n * Predefined category configs for other profiles (teacher, manager)\n */\nexport const DEFAULT_CATEGORIES: TimeChartCategory[] = [\n {\n key: TIME_CHART_CATEGORY_KEY.ACTIVITIES,\n label: 'Atividades',\n colorClass: 'bg-success-700',\n },\n {\n key: TIME_CHART_CATEGORY_KEY.RECOMMENDED_LESSONS,\n label: 'Aulas recomendadas',\n colorClass: 'bg-indicator-positive',\n },\n];\n\n// ─── API Types ───────────────────────────────────────────────\n\n/**\n * Request body for POST /access-report/access/chart\n */\nexport interface TimeChartRequest {\n period: StudentsHighlightPeriod;\n targetProfile: PROFILE_ROLES;\n schoolGroupIds?: string[];\n schoolIds?: string[];\n allSchoolGroupsSelected?: boolean;\n allSchoolsSelected?: boolean;\n}\n\n/**\n * A single period entry for student profile response\n */\nexport interface TimeChartStudentPeriodItem {\n label: string;\n activities: number;\n content: number;\n simulations: number;\n questionnaires: number;\n recommendedLessons: number;\n}\n\n/**\n * Hours by item breakdown for student profile (percentages 0-100)\n */\nexport interface TimeChartStudentItemBreakdown {\n activities: number;\n content: number;\n simulations: number;\n questionnaires: number;\n recommendedLessons: number;\n}\n\n/**\n * API response data for student profile\n */\nexport interface TimeChartStudentData {\n labels: string[];\n hoursByPeriod: TimeChartStudentPeriodItem[];\n hoursByItem: TimeChartStudentItemBreakdown;\n}\n\n/**\n * A single period entry for non-student profile response\n */\nexport interface TimeChartDefaultPeriodItem {\n label: string;\n activities: number;\n recommendedLessons: number;\n}\n\n/**\n * Hours by item breakdown for non-student profiles (percentages 0-100)\n */\nexport interface TimeChartDefaultItemBreakdown {\n activities: number;\n recommendedLessons: number;\n}\n\n/**\n * API response data for non-student profiles\n */\nexport interface TimeChartDefaultData {\n labels: string[];\n hoursByPeriod: TimeChartDefaultPeriodItem[];\n hoursByItem: TimeChartDefaultItemBreakdown;\n}\n\n/**\n * Full API response wrapper\n */\nexport interface TimeChartResponse<\n T extends TimeChartStudentData | TimeChartDefaultData,\n> {\n message: string;\n data: T;\n}\n\n// ─── Utilities ───────────────────────────────────────────────\n\n/**\n * Calculate Y-axis tick values formatted for hours display.\n * Rounds up to the nearest multiple of 4 so that dividing into 4 equal\n * intervals always produces integer ticks with uniform spacing.\n */\nexport const calculateHourTicks = (maxHours: number): number[] => {\n if (maxHours <= 0) return [0];\n\n const niceMax = Math.ceil(maxHours / 4) * 4;\n const step = niceMax / 4;\n\n return [niceMax, step * 3, step * 2, step, 0];\n};\n\n/**\n * Safely reads a numeric value from a day data entry by category key\n */\nconst getDayValue = (day: TimeChartDayData, key: string): number =>\n Number(day[key]) || 0;\n\n// ─── Sub-components ──────────────────────────────────────────\n\nconst LegendItem = ({ color, label }: { color: string; label: string }) => (\n <div className=\"flex flex-row items-center gap-2\">\n <div className={cn('w-2 h-2 rounded-full', color)} />\n <Text size=\"sm\" weight=\"medium\" className=\"text-text-600\">\n {label}\n </Text>\n </div>\n);\n\nconst ChartCard = ({\n title,\n categories,\n children,\n className,\n contentGapClassName = 'gap-4',\n ...props\n}: {\n title: string;\n categories: TimeChartCategory[];\n children: ReactNode;\n className?: string;\n /** Vertical gap between the header block and the chart area */\n contentGapClassName?: string;\n} & HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n 'flex flex-col p-5 bg-background border border-border-50 rounded-xl',\n contentGapClassName,\n className\n )}\n {...props}\n >\n {/* Header: title + legends */}\n <div className=\"flex flex-col gap-4\">\n <Text\n as=\"h3\"\n size=\"lg\"\n weight=\"bold\"\n className=\"text-text-950 tracking-[0.2px]\"\n >\n {title}\n </Text>\n <div className=\"flex flex-row flex-wrap justify-between gap-x-6 gap-y-2\">\n {categories.map((cat) => (\n <LegendItem key={cat.key} color={cat.colorClass} label={cat.label} />\n ))}\n </div>\n </div>\n {children}\n </div>\n);\n\nconst YAxis = ({\n ticks,\n chartHeight,\n unitSuffix,\n}: {\n ticks: number[];\n chartHeight: number;\n unitSuffix: string;\n}) => (\n <div\n className=\"flex flex-col justify-between items-end pr-3\"\n style={{ height: chartHeight }}\n aria-hidden=\"true\"\n >\n {ticks.map((tick, index) => (\n <Text\n key={`${tick}-${index}`}\n size=\"md\"\n weight=\"normal\"\n className=\"text-text-700\"\n >\n {tick === 0 ? '0' : `${tick}${unitSuffix}`}\n </Text>\n ))}\n </div>\n);\n\nconst GridLines = ({\n ticks,\n chartHeight,\n}: {\n ticks: number[];\n chartHeight: number;\n}) => (\n <div\n className=\"absolute inset-0 flex flex-col justify-between pointer-events-none\"\n style={{ height: chartHeight }}\n >\n {ticks.map((tick, index) => (\n <div\n key={`grid-${tick}-${index}`}\n className=\"w-full border-t border-dashed border-border-200\"\n />\n ))}\n </div>\n);\n\nconst StackedBar = ({\n day,\n categories,\n maxValue,\n chartHeight,\n unitSuffix,\n}: {\n day: TimeChartDayData;\n categories: TimeChartCategory[];\n maxValue: number;\n chartHeight: number;\n unitSuffix: string;\n}) => {\n const nonZeroCategories = categories.filter(\n (cat) => getDayValue(day, cat.key) > 0\n );\n\n const tooltipContent =\n nonZeroCategories.length > 0 ? (\n <div className=\"flex flex-col gap-1\">\n {nonZeroCategories.map((cat) => (\n <div key={cat.key} className=\"flex items-center gap-2\">\n <div\n className=\"w-2 h-2 rounded-full shrink-0\"\n style={{ background: bgClassToCssVar(cat.colorClass) }}\n />\n <Text as=\"span\" size=\"xs\" weight=\"medium\" color=\"text-white\">\n {cat.label}: {getDayValue(day, cat.key)}\n {unitSuffix}\n </Text>\n </div>\n ))}\n </div>\n ) : null;\n\n return (\n <Tooltip\n content={tooltipContent ?? ''}\n disabled={!tooltipContent}\n position=\"top\"\n className=\"flex-1\"\n contentClassName=\"whitespace-normal\"\n >\n <div className=\"flex flex-col items-center gap-2 w-full cursor-pointer group/bar\">\n <div\n className=\"w-full flex flex-col-reverse items-center justify-start relative\"\n style={{ height: chartHeight }}\n >\n {categories.map((cat) => {\n const value = getDayValue(day, cat.key);\n const segmentHeight =\n maxValue === 0 ? 0 : (value / maxValue) * chartHeight;\n if (segmentHeight === 0) return null;\n\n const isFirst = cat === nonZeroCategories[0];\n // Index access instead of .at() for old-browser support\n // (Chrome < 92 / Android 7), see FRONTEND-LOGIN-WEB-20.\n const isLast =\n cat === nonZeroCategories[nonZeroCategories.length - 1];\n\n return (\n <div\n key={cat.key}\n data-testid={`bar-segment-${day.label}-${cat.key}`}\n className={cn(\n 'w-9',\n cat.colorClass,\n isFirst && 'rounded-b',\n isLast && 'rounded-t',\n isFirst && isLast && 'rounded'\n )}\n style={{ height: `${segmentHeight}px` }}\n aria-label={`${cat.label}: ${value}${unitSuffix}`}\n />\n );\n })}\n {nonZeroCategories.length > 0 && (\n <div className=\"absolute inset-0 bg-white/50 opacity-0 group-hover/bar:opacity-100 transition-opacity duration-200 rounded-md pointer-events-none z-20\" />\n )}\n </div>\n <Text\n size=\"md\"\n weight=\"normal\"\n className=\"text-text-700 text-center\"\n data-testid={`day-label-${day.label}`}\n >\n {day.label}\n </Text>\n </div>\n </Tooltip>\n );\n};\n\nconst PieChart = ({\n categories,\n totals,\n size,\n}: {\n categories: TimeChartCategory[];\n totals: Record<string, number>;\n size: number;\n}) => {\n const [hoveredSlice, setHoveredSlice] = useState<string | null>(null);\n\n const grandTotal = categories.reduce(\n // istanbul ignore next -- defensive: parent always provides all keys\n (sum, cat) => sum + (totals[cat.key] ?? 0),\n 0\n );\n\n if (grandTotal === 0) {\n const radius = size * 0.4;\n const center = size / 2;\n return (\n <svg\n width={size}\n height={size}\n viewBox={`0 0 ${size} ${size}`}\n aria-hidden=\"true\"\n data-testid=\"pie-chart\"\n >\n <circle\n cx={center}\n cy={center}\n r={radius}\n className=\"fill-background-300\"\n />\n </svg>\n );\n }\n\n const radius = size * 0.4;\n const center = size / 2;\n let cumulativeAngle = 0;\n\n const slices = categories\n .map((cat) => {\n // istanbul ignore next -- defensive: parent always provides all keys\n const value = totals[cat.key] ?? 0;\n const percentage = (value / grandTotal) * 100;\n const angle = (percentage / 100) * 360;\n const startAngle = cumulativeAngle;\n const endAngle = cumulativeAngle + angle;\n const midAngle = startAngle + angle / 2;\n\n cumulativeAngle = endAngle;\n\n return { cat, percentage, startAngle, endAngle, midAngle };\n })\n .filter((s) => s.percentage > 0);\n\n const hoveredData = hoveredSlice\n ? slices.find((s) => s.cat.key === hoveredSlice)\n : null;\n\n const tooltipPos = hoveredData\n ? polarToCartesian(center, center, radius * 0.6, hoveredData.midAngle)\n : { x: 0, y: 0 };\n\n return (\n <div className=\"relative inline-block\">\n <svg\n width={size}\n height={size}\n viewBox={`0 0 ${size} ${size}`}\n aria-hidden=\"true\"\n data-testid=\"pie-chart\"\n onMouseLeave={() => setHoveredSlice(null)}\n >\n {slices.map((slice) => {\n const path =\n slice.percentage >= 99.99\n ? undefined\n : describeArc(\n center,\n center,\n radius,\n slice.startAngle,\n slice.endAngle\n );\n\n const labelPos = polarToCartesian(\n center,\n center,\n radius * 0.6,\n slice.midAngle\n );\n\n return (\n <g\n key={slice.cat.key}\n data-testid={`pie-slice-${slice.cat.key}`}\n onMouseEnter={() => setHoveredSlice(slice.cat.key)}\n className=\"cursor-pointer\"\n >\n {slice.percentage >= 99.99 ? (\n <circle\n cx={center}\n cy={center}\n r={radius}\n style={{ fill: bgClassToCssVar(slice.cat.colorClass) }}\n />\n ) : (\n <path\n d={path}\n style={{ fill: bgClassToCssVar(slice.cat.colorClass) }}\n />\n )}\n {hoveredSlice === slice.cat.key &&\n (slice.percentage >= 99.99 ? (\n <circle\n cx={center}\n cy={center}\n r={radius}\n fill=\"white\"\n opacity={0.5}\n style={{ pointerEvents: 'none' }}\n />\n ) : (\n <path\n d={path}\n fill=\"white\"\n opacity={0.5}\n style={{ pointerEvents: 'none' }}\n />\n ))}\n {slice.percentage >= 5 && (\n <text\n x={labelPos.x}\n y={labelPos.y}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n className=\"fill-white text-xs font-bold\"\n style={{ fontSize: '12px', pointerEvents: 'none' }}\n >\n {`${Math.round(slice.percentage)}%`}\n </text>\n )}\n </g>\n );\n })}\n </svg>\n {hoveredData && (\n <div\n className=\"absolute bg-background-900 text-white px-4 py-2 rounded-lg shadow-[0px_3px_10px_0px_rgba(38,38,38,0.2)] pointer-events-none z-10\"\n style={{\n left: tooltipPos.x,\n top: tooltipPos.y,\n transform: 'translate(-50%, calc(-100% - 8px))',\n }}\n >\n <div className=\"flex items-center gap-2\">\n <div\n className=\"w-2 h-2 rounded-full shrink-0\"\n style={{\n background: bgClassToCssVar(hoveredData.cat.colorClass),\n }}\n />\n <Text\n as=\"span\"\n size=\"xs\"\n weight=\"bold\"\n color=\"text-white\"\n className=\"whitespace-nowrap\"\n >\n {hoveredData.cat.label}: {Math.round(hoveredData.percentage)}%\n </Text>\n </div>\n </div>\n )}\n </div>\n );\n};\n\n// ─── Main component ──────────────────────────────────────────\n\n/**\n * TimeChart component - displays time data as a stacked bar chart (hours per period)\n * and a pie chart (hours by category) side by side.\n *\n * Data can be passed directly from the API response without transformation:\n *\n * @example\n * ```tsx\n * // Student profile - pass API data directly\n * <TimeChart\n * data={{\n * categories: STUDENT_CATEGORIES,\n * hoursByPeriod: apiResponse.data.hoursByPeriod,\n * hoursByItem: apiResponse.data.hoursByItem,\n * }}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Teacher/Manager profile\n * <TimeChart\n * data={{\n * categories: DEFAULT_CATEGORIES,\n * hoursByPeriod: apiResponse.data.hoursByPeriod,\n * hoursByItem: apiResponse.data.hoursByItem,\n * }}\n * />\n * ```\n */\nexport const TimeChart = ({\n data,\n barChartTitle = 'Dados de horas por semana',\n pieChartTitle = 'Dados de horas por item',\n chartHeight = 180,\n pieSize = 200,\n unitSuffix = 'h',\n className,\n ...props\n}: TimeChartProps) => {\n const { categories, hoursByPeriod, hoursByItem } = data;\n\n // Calculate max stacked value across all periods for Y-axis scale\n const periodTotals = hoursByPeriod.map((day) =>\n categories.reduce((sum, cat) => sum + getDayValue(day, cat.key), 0)\n );\n const maxPeriodTotal = Math.max(...periodTotals, 0);\n const yAxisTicks = calculateHourTicks(maxPeriodTotal);\n const adjustedMax = yAxisTicks[0];\n\n // Use pre-calculated percentages from API hoursByItem or compute from period totals\n const categoryTotals: Record<string, number> = {};\n if (hoursByItem) {\n for (const cat of categories) {\n categoryTotals[cat.key] = hoursByItem[cat.key] ?? 0;\n }\n } else {\n for (const cat of categories) {\n categoryTotals[cat.key] = hoursByPeriod.reduce(\n (sum, day) => sum + getDayValue(day, cat.key),\n 0\n );\n }\n }\n\n return (\n <div\n className={cn('grid grid-cols-1 lg:grid-cols-2 gap-4', className)}\n aria-label=\"Gráficos de dados de horas por semana e por categoria\"\n {...props}\n >\n {/* Stacked Bar Chart */}\n <ChartCard\n title={barChartTitle}\n categories={categories}\n contentGapClassName=\"gap-8\"\n >\n <div className=\"flex flex-row\">\n <YAxis\n ticks={yAxisTicks}\n chartHeight={chartHeight}\n unitSuffix={unitSuffix}\n />\n <div className=\"w-4\" />\n <div className=\"flex-1 relative\">\n <GridLines ticks={yAxisTicks} chartHeight={chartHeight} />\n <div className=\"flex flex-row flex-1 gap-2 relative z-10\">\n {hoursByPeriod.map((day, index) => (\n <StackedBar\n key={`${day.label}-${index}`}\n day={day}\n categories={categories}\n maxValue={adjustedMax}\n chartHeight={chartHeight}\n unitSuffix={unitSuffix}\n />\n ))}\n </div>\n </div>\n </div>\n </ChartCard>\n\n {/* Pie Chart */}\n <ChartCard title={pieChartTitle} categories={categories}>\n <div className=\"flex items-center justify-center py-4\">\n <PieChart\n categories={categories}\n totals={categoryTotals}\n size={pieSize}\n />\n </div>\n </ChartCard>\n </div>\n );\n};\n\nexport default TimeChart;\n"],"mappings":"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","names":["TIME_CHART_CATEGORY_KEY","radius","center"]}