analytica-frontend-lib 1.6.19 → 1.6.21
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 +9 -9
- package/dist/ActivitiesHistory/index.mjs +8 -8
- package/dist/ActivityCardQuestionBanks/index.js +9 -9
- package/dist/ActivityCardQuestionBanks/index.mjs +8 -8
- package/dist/ActivityCardQuestionPreview/index.js +9 -9
- package/dist/ActivityCardQuestionPreview/index.mjs +8 -8
- package/dist/ActivityCreate/ActivityCreateContent/index.js +9 -9
- package/dist/ActivityCreate/ActivityCreateContent/index.mjs +8 -8
- package/dist/ActivityCreate/index.js +9 -9
- package/dist/ActivityCreate/index.mjs +8 -8
- package/dist/ActivityDetails/index.js +9 -9
- package/dist/ActivityDetails/index.mjs +8 -8
- package/dist/ActivityFilters/index.js +9 -9
- package/dist/ActivityFilters/index.mjs +8 -8
- package/dist/ActivityPageLayout/index.js +9 -9
- package/dist/ActivityPageLayout/index.mjs +8 -8
- package/dist/ActivityPreview/index.js +9 -9
- package/dist/ActivityPreview/index.mjs +8 -8
- package/dist/AlertManager/index.js +9 -9
- package/dist/AlertManager/index.mjs +8 -8
- package/dist/AppHeader/index.d.ts.map +1 -1
- package/dist/AppHeader/index.js +5 -5
- package/dist/AppHeader/index.mjs +4 -4
- package/dist/CalendarCard/index.js +3 -3
- package/dist/CalendarCard/index.mjs +2 -2
- package/dist/CheckBoxGroup/index.js +9 -9
- package/dist/CheckBoxGroup/index.mjs +8 -8
- package/dist/DropdownMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/index.js +2 -2
- package/dist/DropdownMenu/index.mjs +1 -1
- package/dist/ExamDetailsLayout/ExamDetailsPage/index.js +9 -9
- package/dist/ExamDetailsLayout/ExamDetailsPage/index.mjs +8 -8
- package/dist/ExamPageLayout/index.js +9 -9
- package/dist/ExamPageLayout/index.mjs +8 -8
- package/dist/Forum/index.js +3 -3
- package/dist/Forum/index.mjs +2 -2
- package/dist/NotificationCard/index.js +3 -3
- package/dist/NotificationCard/index.mjs +2 -2
- package/dist/PrintableUsersTable/index.js +9 -9
- package/dist/PrintableUsersTable/index.mjs +8 -8
- package/dist/QuestionsPerformanceCard/index.js +9 -9
- package/dist/QuestionsPerformanceCard/index.mjs +8 -8
- package/dist/RecommendedLessonCreate/index.js +9 -9
- package/dist/RecommendedLessonCreate/index.mjs +8 -8
- package/dist/RecommendedLessonsHistory/index.js +9 -9
- package/dist/RecommendedLessonsHistory/index.mjs +8 -8
- package/dist/Search/index.js +3 -3
- package/dist/Search/index.mjs +2 -2
- package/dist/SendActivityModal/SendActivityModal.js +9 -9
- package/dist/SendActivityModal/SendActivityModal.mjs +8 -8
- package/dist/SendActivityModal/index.js +9 -9
- package/dist/SendActivityModal/index.mjs +8 -8
- package/dist/SendLessonModal/index.js +9 -9
- package/dist/SendLessonModal/index.mjs +8 -8
- package/dist/SimulatedPerformance/SimulatedPerformanceView/index.js +9 -9
- package/dist/SimulatedPerformance/SimulatedPerformanceView/index.mjs +8 -8
- package/dist/SimulatedStudentSimulationsModal/index.js +15 -15
- package/dist/SimulatedStudentSimulationsModal/index.mjs +1 -1
- package/dist/SimulationsPage/index.js +9 -9
- package/dist/SimulationsPage/index.mjs +8 -8
- package/dist/TableProvider/index.js +9 -9
- package/dist/TableProvider/index.mjs +8 -8
- package/dist/UnifiedDraftModelPage/index.js +9 -9
- package/dist/UnifiedDraftModelPage/index.mjs +8 -8
- package/dist/UnifiedHistoryPage/index.js +9 -9
- package/dist/UnifiedHistoryPage/index.mjs +8 -8
- package/dist/{chunk-OMNDC4BZ.js → chunk-7XWWI5EY.js} +25 -28
- package/dist/chunk-7XWWI5EY.js.map +1 -0
- package/dist/{chunk-6LJZGFSL.mjs → chunk-CFTQ24UT.mjs} +2 -2
- package/dist/{chunk-LLUJLIDC.mjs → chunk-CKSOC6YI.mjs} +5 -4
- package/dist/{chunk-LLUJLIDC.mjs.map → chunk-CKSOC6YI.mjs.map} +1 -1
- package/dist/{chunk-DXSNLRL4.mjs → chunk-CWGYVJTQ.mjs} +25 -28
- package/dist/chunk-CWGYVJTQ.mjs.map +1 -0
- package/dist/{chunk-OYEDIVC2.js → chunk-CWTCN6F7.js} +5 -5
- package/dist/{chunk-OYEDIVC2.js.map → chunk-CWTCN6F7.js.map} +1 -1
- package/dist/{chunk-QKRUOG3N.mjs → chunk-E4MLDRLD.mjs} +2 -2
- package/dist/{chunk-GP47TASX.mjs → chunk-ENCFJBY5.mjs} +2 -2
- package/dist/{chunk-FBA6QDJM.js → chunk-IUH7JW7H.js} +16 -15
- package/dist/chunk-IUH7JW7H.js.map +1 -0
- package/dist/{chunk-UEUGH2QZ.js → chunk-KYGFYAU7.js} +7 -7
- package/dist/{chunk-UEUGH2QZ.js.map → chunk-KYGFYAU7.js.map} +1 -1
- package/dist/{chunk-T3V5QTJZ.mjs → chunk-MUVQIQAG.mjs} +2 -2
- package/dist/{chunk-7HW4PZLV.js → chunk-MYQ2LJWO.js} +6 -6
- package/dist/{chunk-7HW4PZLV.js.map → chunk-MYQ2LJWO.js.map} +1 -1
- package/dist/{chunk-K2ILHIT6.mjs → chunk-NAOHJW6H.mjs} +4 -4
- package/dist/{chunk-S4CNM57F.js → chunk-QHXJB5QM.js} +10 -10
- package/dist/{chunk-S4CNM57F.js.map → chunk-QHXJB5QM.js.map} +1 -1
- package/dist/{chunk-IXZT6MKP.js → chunk-QN32O3J3.js} +19 -19
- package/dist/{chunk-IXZT6MKP.js.map → chunk-QN32O3J3.js.map} +1 -1
- package/dist/{chunk-PTFHHHQG.mjs → chunk-X2YQT5BS.mjs} +22 -7
- package/dist/chunk-X2YQT5BS.mjs.map +1 -0
- package/dist/{chunk-M5BZW7BG.js → chunk-Y64LT6BL.js} +22 -7
- package/dist/chunk-Y64LT6BL.js.map +1 -0
- package/dist/components/SimulationsPage/SimulationsDetailModal.d.ts.map +1 -1
- package/dist/hooks/useAppContent/index.js +9 -9
- package/dist/hooks/useAppContent/index.mjs +8 -8
- package/dist/index.js +9 -9
- package/dist/index.mjs +8 -8
- package/dist/utils/questionDuration.d.ts +14 -12
- package/dist/utils/questionDuration.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-DXSNLRL4.mjs.map +0 -1
- package/dist/chunk-FBA6QDJM.js.map +0 -1
- package/dist/chunk-M5BZW7BG.js.map +0 -1
- package/dist/chunk-OMNDC4BZ.js.map +0 -1
- package/dist/chunk-PTFHHHQG.mjs.map +0 -1
- /package/dist/{chunk-6LJZGFSL.mjs.map → chunk-CFTQ24UT.mjs.map} +0 -0
- /package/dist/{chunk-QKRUOG3N.mjs.map → chunk-E4MLDRLD.mjs.map} +0 -0
- /package/dist/{chunk-GP47TASX.mjs.map → chunk-ENCFJBY5.mjs.map} +0 -0
- /package/dist/{chunk-T3V5QTJZ.mjs.map → chunk-MUVQIQAG.mjs.map} +0 -0
- /package/dist/{chunk-K2ILHIT6.mjs.map → chunk-NAOHJW6H.mjs.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-FBA6QDJM.js","../src/components/AppHeader/AppHeader.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-FBA6QDJM.js","sourcesContent":[null,"import type { ReactNode } from 'react';\nimport { useCallback, useEffect, useState } from 'react';\nimport { CaretRightIcon } from '@phosphor-icons/react/dist/csr/CaretRight';\nimport { UserIcon as UserPhosphorIcon } from '@phosphor-icons/react/dist/csr/User';\n\nimport { BrandingLogo } from '../BrandingLogo/BrandingLogo';\nimport CalendarCard from '../CalendarCard/CalendarCard';\nimport IconButton from '../IconButton/IconButton';\nimport DropdownMenu, {\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n ProfileMenuFooter,\n ProfileMenuHeader,\n ProfileMenuInfo,\n ProfileMenuSection,\n ProfileToggleTheme,\n} from '../DropdownMenu/DropdownMenu';\nimport NotificationCard, {\n type NotificationGroup,\n} from '../NotificationCard/NotificationCard';\nimport { PageContainer } from '../PageContainer/PageContainer';\nimport Text from '../Text/Text';\nimport { UserIcon } from '../UserIcon/UserIcon';\nimport { useMobile } from '../../hooks/useMobile';\nimport { syncDropdownState } from '../../utils/dropdown';\nimport {\n NotificationEntityType,\n NotificationEntityStatus,\n} from '../../types/notifications';\n\n/**\n * Subset of session info consumed by the application header.\n * Each property is optional so the consumer can pass whatever it has.\n */\nexport interface AppHeaderSessionInfo {\n userName?: string;\n email?: string;\n urlProfilePicture?: string;\n schoolName?: string;\n className?: string;\n schoolYearName?: string;\n}\n\n/**\n * Minimal user shape needed to fill the profile dropdown header.\n */\nexport interface AppHeaderUser {\n name?: string;\n email?: string;\n}\n\n/**\n * Tutorial action rendered as a pill button in the header (left of the\n * notifications bell). The consumer owns the click behavior (usually opening\n * the configured tutorial URL in a new tab).\n */\nexport interface AppHeaderTutorial {\n /** Show the tutorial button. */\n visible?: boolean;\n /** Button label. Defaults to \"Tutoriais\". */\n label?: string;\n /** Fired when the tutorial button is clicked. */\n onClick: () => void;\n}\n\n/**\n * Notifications data + callbacks the AppHeader forwards to NotificationCard.\n * Built by the consumer (usually via `createNotificationsHook(api)`).\n */\nexport interface AppHeaderNotifications {\n unreadCount: number;\n loading: boolean;\n error: string | null;\n groupedNotifications: NotificationGroup[];\n refreshNotifications: () => void;\n markAsRead: (id: string) => void;\n deleteNotification: (id: string) => void;\n fetchNotifications: () => void;\n fetchUnreadCount?: () => void;\n /**\n * Optional bulk action. When provided, the \"Marcar todas como lidas\" button\n * renders in the notification center; when omitted, it stays hidden, which is\n * the behaviour every consumer had before this prop existed.\n */\n markAllAsRead?: () => void;\n getActionLabel: (entityType?: NotificationEntityType) => string | undefined;\n}\n\nexport interface AppHeaderProps {\n /** Authenticated user (fallback when sessionInfo doesn't carry name/email). */\n user?: AppHeaderUser;\n /** Session metadata used to render profile name, email, photo and school info. */\n sessionInfo?: AppHeaderSessionInfo;\n /** Notifications wiring (state + callbacks). */\n notifications: AppHeaderNotifications;\n /** Tutorial pill button shown before the notifications bell. */\n tutorial?: AppHeaderTutorial;\n /** Show the calendar widget in the header. */\n showCalendar?: boolean;\n /** Content rendered inside the calendar (dropdown on tablet/desktop, modal on mobile). */\n calendarContent?: ReactNode;\n /**\n * Optional controlled open state for the calendar.\n * When provided, the consumer is the source of truth; useful for closing\n * the calendar programmatically (e.g. on route change).\n */\n calendarOpen?: boolean;\n /** Fired whenever the calendar open state changes. */\n onCalendarOpenChange?: (open: boolean) => void;\n /** Show school/class/year inside the profile dropdown. */\n showProfileInfo?: boolean;\n /** Optional fallback image for the notifications empty state. */\n emptyNotificationsImage?: string;\n /** Empty-state title for notifications dropdown. */\n emptyNotificationsTitle?: string;\n /** Empty-state description for notifications dropdown. */\n emptyNotificationsDescription?: string;\n /** Callback fired when the user activates logout. */\n onLogout: () => void;\n /** Callback fired when the user activates the \"My data\" menu item. */\n onNavigateToMyData: () => void;\n /** Callback fired when the user clicks on a notification with entity info. */\n onNavigateByNotification?: (\n entityType?: NotificationEntityType,\n entityId?: string,\n entityStatus?: NotificationEntityStatus | null,\n questionId?: string | null\n ) => void;\n /**\n * Tailwind class overriding the default `max-w-[1000px]` of the header\n * content (forwarded to the internal `PageContainer`'s `innerClassName`).\n * Pass e.g. `max-w-[1350px]` to align the header with a widened page\n * content. When omitted, the default `max-w-[1000px]` is kept, so other\n * consumers are unaffected.\n */\n contentMaxWidth?: string;\n}\n\nconst DEFAULT_EMPTY_TITLE = 'Nenhuma notificação no momento';\nconst DEFAULT_EMPTY_DESCRIPTION =\n 'Você está em dia com todas as novidades. Volte depois para conferir atualizações!';\n\n/**\n * Application header shared across the analytica frontends.\n *\n * Renders the institution logo, optional calendar dropdown (mobile/tablet),\n * notifications center and a profile menu. Width is constrained by\n * `<PageContainer>` so it always aligns with the rest of the page content.\n */\nexport const AppHeader = ({\n user,\n sessionInfo,\n notifications,\n tutorial,\n showCalendar = false,\n calendarContent,\n calendarOpen,\n onCalendarOpenChange,\n showProfileInfo = false,\n emptyNotificationsImage,\n emptyNotificationsTitle = DEFAULT_EMPTY_TITLE,\n emptyNotificationsDescription = DEFAULT_EMPTY_DESCRIPTION,\n onLogout,\n onNavigateToMyData,\n onNavigateByNotification,\n contentMaxWidth,\n}: AppHeaderProps) => {\n const { isMobile, isExtraSmallMobile } = useMobile();\n const [activeStates, setActiveStates] = useState<Record<string, boolean>>({});\n\n useEffect(() => {\n notifications.fetchUnreadCount?.();\n }, []);\n\n const toggleActive = (buttonId: string) => {\n setActiveStates((prev) => {\n if (prev[buttonId]) {\n return { ...prev, [buttonId]: false };\n }\n return {\n calendar: false,\n notifications: false,\n profile: false,\n [buttonId]: true,\n };\n });\n };\n\n // Stable callback so `DropdownMenu`'s `useEffect [open, onOpenChange]`\n // doesn't re-fire on every parent re-render, which would cascade into a\n // Maximum-update-depth loop (same root cause class as PR #435).\n const handleCalendarOpenChange = useCallback(\n (open: boolean) => {\n setActiveStates((prev) => {\n const current = prev.calendar ?? false;\n if (current === open) {\n return prev;\n }\n if (open) {\n return {\n calendar: true,\n notifications: false,\n profile: false,\n };\n }\n return { ...prev, calendar: false };\n });\n onCalendarOpenChange?.(open);\n },\n [onCalendarOpenChange]\n );\n\n useEffect(() => {\n const handleEscapeKey = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setActiveStates({});\n }\n };\n document.addEventListener('keydown', handleEscapeKey);\n return () => document.removeEventListener('keydown', handleEscapeKey);\n }, []);\n\n const handleMyDataClick = () => {\n setActiveStates({});\n onNavigateToMyData();\n };\n\n const displayName = sessionInfo?.userName ?? user?.name ?? 'Usuário';\n const displayEmail = sessionInfo?.email ?? user?.email ?? '';\n\n const pickResponsiveClass = (\n extraSmall: string,\n mobile: string,\n desktop: string\n ): string => {\n if (isExtraSmallMobile) return extraSmall;\n if (isMobile) return mobile;\n return desktop;\n };\n const profileDropdownWidth = pickResponsiveClass(\n 'min-w-[260px]',\n 'min-w-[280px]',\n 'min-w-[320px]'\n );\n const sectionGap = pickResponsiveClass('gap-1', 'gap-2', 'gap-3');\n const logoHeight = pickResponsiveClass('h-6', 'h-8', 'h-10');\n const tutorialPadding = pickResponsiveClass(\n 'px-3 py-1.5 text-xs',\n 'px-3.5 py-2 text-sm',\n 'px-4 py-2 text-sm'\n );\n\n return (\n <header\n data-component=\"AppHeader\"\n className=\"bg-primary-800 w-full h-[70px] flex justify-center items-center\"\n >\n <PageContainer\n className=\"pb-0 justify-center\"\n innerClassName={contentMaxWidth}\n >\n <div className=\"w-full flex flex-row justify-between items-center gap-3\">\n {/* `min-w-0 shrink` lets the logo compress instead of overflowing:\n an <img> resolves `min-width:auto` to its intrinsic width, which\n would otherwise block flex-shrink and push the actions off-screen\n on narrow viewports. `object-left` keeps it left-aligned as it\n scales down. The actions never shrink. */}\n <BrandingLogo\n variant=\"main\"\n alt=\"Logo\"\n className={`${logoHeight} object-contain object-left min-w-0 shrink`}\n />\n <section\n className={`flex flex-row items-center shrink-0 ${sectionGap}`}\n >\n {tutorial?.visible && (\n <button\n type=\"button\"\n data-testid=\"app-header-tutorial\"\n onClick={tutorial.onClick}\n /* `primary` (não `text`) é o foreground de `primary-800` em\n todos os temas — os dois formam um par contrastante mesmo\n onde o dark inverte o header (paraiba/analytica). Usar\n `text-*` aqui inverteria com o modo enquanto o fundo do\n header não inverte, apagando o botão no dark. */\n className={`rounded-full border border-primary text-primary font-medium whitespace-nowrap cursor-pointer transition-colors hover:bg-primary-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary ${tutorialPadding}`}\n >\n {tutorial.label ?? 'Tutoriais'}\n </button>\n )}\n\n {showCalendar && (\n <div className=\"lg:hidden\">\n <CalendarCard\n content={calendarContent}\n isOpen={calendarOpen ?? activeStates.calendar ?? false}\n onOpenChange={handleCalendarOpenChange}\n />\n </div>\n )}\n\n <NotificationCard\n mode=\"center\"\n isActive={activeStates.notifications}\n onToggleActive={() => toggleActive('notifications')}\n onOpenChange={(open: boolean) => {\n syncDropdownState(\n open,\n activeStates.notifications,\n setActiveStates,\n 'notifications'\n );\n }}\n unreadCount={notifications.unreadCount}\n groupedNotifications={notifications.groupedNotifications}\n loading={notifications.loading}\n error={notifications.error}\n onRetry={notifications.refreshNotifications}\n onMarkAsReadById={notifications.markAsRead}\n onMarkAllAsRead={notifications.markAllAsRead}\n onDeleteById={notifications.deleteNotification}\n onNavigateById={(\n entityType,\n entityId,\n entityStatus,\n questionId\n ) => {\n if (entityType && entityId) {\n // Fecha o painel antes de navegar. Sem isso, quando o destino\n // é a própria página em que o usuário já está (ex.: ele\n // termina a atividade, cai no resultado, abre o sino e clica\n // em \"Ver resultado\" da notificação dessa mesma atividade),\n // o navigate para a rota atual não muda nada na tela, o\n // painel segue aberto e ele clica de novo — rage click\n // FRONTEND-ALUNO-WEB-F3, 91 ocorrências / 36 usuários em seis\n // semanas. Fechar o painel é o feedback visível que faltava,\n // e vale para qualquer destino.\n //\n // É um setState em handler de clique, não em render/efeito:\n // não reabre o loop do PR #435. E o onOpenChange que o\n // DropdownMenu dispara ao fechar vira no-op no\n // syncDropdownState, porque isActive já estará false.\n setActiveStates((prev) => ({\n ...prev,\n notifications: false,\n }));\n onNavigateByNotification?.(\n entityType,\n entityId,\n entityStatus,\n questionId\n );\n }\n }}\n getActionLabel={notifications.getActionLabel}\n onFetchNotifications={notifications.fetchNotifications}\n emptyStateImage={emptyNotificationsImage}\n emptyStateTitle={emptyNotificationsTitle}\n emptyStateDescription={emptyNotificationsDescription}\n />\n\n <DropdownMenu\n onOpenChange={(open: boolean) => {\n syncDropdownState(\n open,\n activeStates.profile,\n setActiveStates,\n 'profile'\n );\n }}\n >\n <DropdownMenuTrigger\n className=\"text-primary cursor-pointer\"\n asChild\n >\n <IconButton\n active={activeStates.profile}\n onClick={() => toggleActive('profile')}\n icon={\n <UserIcon\n size={24}\n className={activeStates.profile ? 'opacity-80' : ''}\n />\n }\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent\n className={profileDropdownWidth}\n side=\"bottom\"\n variant=\"profile\"\n align=\"end\"\n >\n <ProfileMenuHeader\n name={displayName}\n email={displayEmail}\n photoUrl={sessionInfo?.urlProfilePicture}\n />\n\n {showProfileInfo && (\n <ProfileMenuInfo\n schoolName={sessionInfo?.schoolName ?? ''}\n classYearName={sessionInfo?.className ?? ''}\n schoolYearName={sessionInfo?.schoolYearName ?? ''}\n />\n )}\n\n <ProfileMenuSection>\n <DropdownMenuItem\n variant=\"profile\"\n iconLeft={<UserPhosphorIcon />}\n iconRight={<CaretRightIcon />}\n onClick={handleMyDataClick}\n >\n <Text size=\"md\" color=\"text-text-700\">\n Meus dados\n </Text>\n </DropdownMenuItem>\n\n <ProfileToggleTheme />\n </ProfileMenuSection>\n\n <ProfileMenuFooter onClick={onLogout} />\n </DropdownMenuContent>\n </DropdownMenu>\n </section>\n </div>\n </PageContainer>\n </header>\n );\n};\n\nexport default AppHeader;\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-M5BZW7BG.js","../src/components/DropdownMenu/DropdownMenu.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-M5BZW7BG.js","sourcesContent":[null,"import { CaretRightIcon } from '@phosphor-icons/react/dist/csr/CaretRight';\nimport { SignOutIcon } from '@phosphor-icons/react/dist/csr/SignOut';\nimport { UserIcon } from '@phosphor-icons/react/dist/csr/User';\nimport {\n forwardRef,\n ReactNode,\n ButtonHTMLAttributes,\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n HTMLAttributes,\n MouseEvent,\n KeyboardEvent,\n isValidElement,\n Children,\n cloneElement,\n ReactElement,\n useState,\n Ref,\n RefObject,\n CSSProperties,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { create, StoreApi, useStore } from 'zustand';\nimport Button, { ButtonReadingFluency } from '../Button/Button';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\nimport readingFluencyBird from '../../assets/img/readingFluencyBird.png';\nimport Modal from '../Modal/Modal';\nimport { ThemeToggle } from '../ThemeToggle/ThemeToggle';\nimport type { ThemeMode } from '@/hooks/useTheme';\nimport { useTheme } from '../../hooks/useTheme';\n\ninterface DropdownStore {\n open: boolean;\n setOpen: (open: boolean) => void;\n}\n\ntype DropdownStoreApi = StoreApi<DropdownStore>;\n\nexport function createDropdownStore(): DropdownStoreApi {\n return create<DropdownStore>((set) => ({\n open: false,\n // Bail out when the value didn't change. Skipping `set` here prevents\n // zustand from creating a new state object, which in turn keeps\n // `useStore(store, (s) => s)` subscribers from re-rendering on no-op\n // syncs (e.g. the `useEffect [propOpen]` controlled-mode bridge).\n setOpen: (open) => set((state) => (state.open === open ? state : { open })),\n }));\n}\n\nexport const useDropdownStore = (externalStore?: DropdownStoreApi) => {\n if (!externalStore) {\n throw new Error(\n 'Component must be used within a DropdownMenu (store is missing)'\n );\n }\n\n return externalStore;\n};\n\n/**\n * Keep the same style object when nothing moved — a fresh object on every\n * scroll event would re-render the menu at 60fps for no reason.\n */\nconst PORTAL_STYLE_KEYS = [\n 'top',\n 'bottom',\n 'left',\n 'right',\n 'transform',\n 'maxHeight',\n] as const;\n\nconst isSamePortalStyle = (a: CSSProperties, b: CSSProperties): boolean =>\n PORTAL_STYLE_KEYS.every((key) => a[key] === b[key]);\n\n/** Tallest a portaled menu gets when there is room for it. */\nconst DEFAULT_PORTAL_MAX_HEIGHT = 320;\n\n/** Breathing room between the menu and the edge of the viewport. */\nconst VIEWPORT_MARGIN = 8;\n\n/**\n * Never shrink a menu below this, however cramped the trigger is: a 20px-tall\n * scroller is worse than one that overhangs a little.\n */\nconst MIN_PORTAL_MAX_HEIGHT = 120;\n\ntype PortalSide = 'top' | 'right' | 'bottom' | 'left';\ntype PortalAlign = 'start' | 'center' | 'end';\n\ninterface PortalPlacement {\n side: PortalSide;\n align: PortalAlign;\n sideOffset: number;\n maxHeight: number;\n}\n\n/**\n * Height cap for a portaled menu, given the room it has.\n *\n * The minimum is a floor on the *viewport-derived* room only — it keeps a menu\n * squeezed against an edge from collapsing into a sliver. A caller that asks\n * for a shorter menu than the minimum still gets what it asked for: its value\n * is the ceiling, never something to be raised.\n *\n * @param available - Room in px between the trigger and the viewport edge\n * @param maxHeight - Tallest the caller allows the menu to be\n * @returns The `max-height` to apply, in px\n */\nconst clampMenuHeight = (available: number, maxHeight: number): number =>\n Math.min(maxHeight, Math.max(available, MIN_PORTAL_MAX_HEIGHT));\n\n/**\n * Horizontal edge the menu lines up with, for the `top`/`bottom` sides.\n *\n * @param rect - Trigger bounding rect\n * @param align - Requested alignment\n * @returns The horizontal part of the portal style\n */\nconst alignPortalStyle = (rect: DOMRect, align: PortalAlign): CSSProperties => {\n if (align === 'end') return { right: window.innerWidth - rect.right };\n if (align === 'center') {\n return {\n left: rect.left + rect.width / 2,\n transform: 'translateX(-50%)',\n };\n }\n return { left: rect.left };\n};\n\n/**\n * Place a menu above or below its trigger, sized to the room that exists.\n *\n * A portaled menu is `position: fixed`, so whatever falls past the edge of the\n * viewport can never be scrolled to. The menu therefore takes the space it\n * actually has and scrolls inside it, and turns over to the other side when\n * clamping alone would still leave it overhanging.\n *\n * The flip threshold is the minimum height rather than the maximum: turning\n * over as soon as the menu could not open at full height would send it over the\n * content on any trigger past the middle of the screen, which reads as a\n * glitch. `side=\"top\"` always opens above — the caller asked for it.\n *\n * @param rect - Trigger bounding rect\n * @param placement - Requested side/alignment, offset and height ceiling\n * @returns Style for a fixed menu anchored to the trigger's top or bottom edge\n */\nconst verticalPortalStyle = (\n rect: DOMRect,\n { side, align, sideOffset, maxHeight }: PortalPlacement\n): CSSProperties => {\n const spaceBelow =\n window.innerHeight - rect.bottom - sideOffset - VIEWPORT_MARGIN;\n const spaceAbove = rect.top - sideOffset - VIEWPORT_MARGIN;\n const openAbove =\n side === 'top' ||\n (spaceBelow < Math.min(maxHeight, MIN_PORTAL_MAX_HEIGHT) &&\n spaceAbove > spaceBelow);\n\n return {\n maxHeight: clampMenuHeight(openAbove ? spaceAbove : spaceBelow, maxHeight),\n overflowY: 'auto',\n ...(openAbove\n ? { bottom: window.innerHeight - rect.top + sideOffset }\n : { top: rect.bottom + sideOffset }),\n ...alignPortalStyle(rect, align),\n };\n};\n\n/**\n * Place a menu beside its trigger, sized to the room below the trigger's top.\n *\n * Anchored by the edge that faces the trigger: a `left` for `side=\"left\"` would\n * grow the menu back over the trigger it was meant to sit outside of. It runs\n * downwards from the trigger's top, so it needs the same clamping as a menu\n * opening below — without it a long list disappears past the viewport bottom.\n *\n * @param rect - Trigger bounding rect\n * @param placement - Requested side, offset and height ceiling\n * @returns Style for a fixed menu anchored to the trigger's left or right edge\n */\nconst horizontalPortalStyle = (\n rect: DOMRect,\n { side, sideOffset, maxHeight }: PortalPlacement\n): CSSProperties => ({\n top: rect.top,\n maxHeight: clampMenuHeight(\n window.innerHeight - rect.top - VIEWPORT_MARGIN,\n maxHeight\n ),\n overflowY: 'auto',\n ...(side === 'left'\n ? { right: window.innerWidth - rect.left + sideOffset }\n : { left: rect.right + sideOffset }),\n});\n\n/**\n * Merge two refs into one. Returns the single ref when only one is set (no\n * callback churn), or `undefined` when neither is — so a cloned child without a\n * ref stays refless.\n */\nconst mergeRefs = <T,>(\n a: Ref<T> | undefined,\n b: Ref<T> | undefined\n): Ref<T> | undefined => {\n if (!a) return b;\n if (!b) return a;\n return (node: T | null) => {\n for (const r of [a, b]) {\n if (typeof r === 'function') r(node);\n else (r as RefObject<T | null>).current = node;\n }\n };\n};\n\nconst injectStore = (\n children: ReactNode,\n store: DropdownStoreApi\n): ReactNode => {\n return Children.map(children, (child) => {\n if (isValidElement(child)) {\n const typedChild = child as ReactElement<{\n store?: DropdownStoreApi;\n children?: ReactNode;\n }>;\n\n // Aqui tu checa o displayName do componente\n const displayName = (\n typedChild.type as unknown as { displayName: string }\n ).displayName;\n\n // Lista de componentes que devem receber o store\n const allowed = [\n 'DropdownMenuTrigger',\n 'DropdownContent',\n 'DropdownMenuContent',\n 'DropdownMenuSeparator',\n 'DropdownMenuItem',\n 'MenuLabel',\n 'ProfileMenuTrigger',\n 'ProfileMenuHeader',\n 'ProfileMenuFooter',\n 'ProfileToggleTheme',\n 'ProfileMenuReadingFluencyTrigger',\n 'ProfileMenuReadingFluencyFooter',\n ];\n\n let newProps: Partial<{\n store: DropdownStoreApi;\n children: ReactNode;\n }> = {};\n\n if (allowed.includes(displayName)) {\n newProps.store = store;\n }\n\n if (typedChild.props.children) {\n newProps.children = injectStore(typedChild.props.children, store);\n }\n\n return cloneElement(typedChild, newProps);\n }\n\n return child;\n });\n};\n\ninterface DropdownMenuProps {\n children: ReactNode;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\nconst DropdownMenu = ({\n children,\n open: propOpen,\n onOpenChange,\n}: DropdownMenuProps) => {\n const storeRef = useRef<DropdownStoreApi | null>(null);\n storeRef.current ??= createDropdownStore();\n const store = storeRef.current;\n const { open, setOpen: storeSetOpen } = useStore(store, (s) => s);\n\n const setOpen = (newOpen: boolean) => {\n storeSetOpen(newOpen);\n };\n\n const menuRef = useRef<HTMLDivElement | null>(null);\n\n const handleArrowDownOrArrowUp = (event: globalThis.KeyboardEvent) => {\n // A portaled content lives in document.body, outside menuRef — look it up\n // globally in that case. Only the open menu carries data-open=\"true\"\n // (a closing one keeps rendering for the 200ms fade-out).\n const menuContent =\n menuRef.current?.querySelector('[role=\"menu\"]') ??\n document.querySelector(\n '[data-dropdown-content=\"true\"][data-open=\"true\"]'\n );\n if (menuContent) {\n event.preventDefault();\n\n const items = Array.from(\n menuContent.querySelectorAll(\n '[role=\"menuitem\"]:not([aria-disabled=\"true\"])'\n )\n ).filter((el): el is HTMLElement => el instanceof HTMLElement);\n\n if (items.length === 0) return;\n\n const focusedItem = document.activeElement as HTMLElement;\n const currentIndex = items.indexOf(focusedItem);\n\n let nextIndex;\n if (event.key === 'ArrowDown') {\n nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % items.length;\n } else {\n // ArrowUp\n nextIndex =\n currentIndex === -1\n ? items.length - 1\n : (currentIndex - 1 + items.length) % items.length;\n }\n\n items[nextIndex]?.focus();\n }\n };\n\n const handleDownkey = (event: globalThis.KeyboardEvent) => {\n if (event.key === 'Escape') {\n setOpen(false);\n } else if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n handleArrowDownOrArrowUp(event);\n }\n };\n\n const handleClickOutside = (event: Event) => {\n const target = event.target as Node;\n\n if (menuRef.current?.contains(target)) {\n return;\n }\n\n if (\n target instanceof Element &&\n target.closest('[data-dropdown-content=\"true\"]')\n ) {\n return;\n }\n\n setOpen(false);\n };\n\n useEffect(() => {\n if (open) {\n document.addEventListener('pointerdown', handleClickOutside);\n document.addEventListener('keydown', handleDownkey);\n }\n\n return () => {\n document.removeEventListener('pointerdown', handleClickOutside);\n document.removeEventListener('keydown', handleDownkey);\n };\n }, [open]);\n\n useEffect(() => {\n onOpenChange?.(open);\n }, [open, onOpenChange]);\n\n useEffect(() => {\n if (propOpen !== undefined) {\n setOpen(propOpen);\n }\n }, [propOpen]);\n\n return (\n <div className=\"relative\" ref={menuRef}>\n {injectStore(children, store)}\n </div>\n );\n};\n\n// Componentes genéricos do DropdownMenu\nconst DropdownMenuTrigger = forwardRef<\n HTMLButtonElement,\n ButtonHTMLAttributes<HTMLButtonElement> & {\n disabled?: boolean;\n store?: DropdownStoreApi;\n /**\n * Render the single child as the trigger instead of wrapping it in a\n * `<button>`. Use when the child is itself a button/interactive element, so\n * the trigger doesn't nest one button inside another (invalid HTML). The\n * child keeps its own props; the toggle behaviour and `aria-expanded` are\n * merged in.\n */\n asChild?: boolean;\n }\n>(\n (\n { className, children, onClick, store: externalStore, asChild, ...props },\n ref\n ) => {\n const store = useDropdownStore(externalStore);\n\n const open = useStore(store, (s) => s.open);\n const toggleOpen = () => store.setState({ open: !open });\n\n const handleTriggerClick = (e: MouseEvent<HTMLElement>) => {\n e.stopPropagation();\n toggleOpen();\n onClick?.(e as MouseEvent<HTMLButtonElement>);\n };\n\n // `injectStore` re-wraps children through Children.map, so `children` may be\n // a single-element array rather than a bare element — unwrap it here.\n const asChildElement = asChild\n ? (Children.toArray(children).find(isValidElement) as\n | ReactElement<{\n className?: string;\n onClick?: (e: MouseEvent<HTMLElement>) => void;\n ref?: Ref<HTMLElement>;\n }>\n | undefined)\n : undefined;\n\n if (asChildElement) {\n const child = asChildElement;\n return cloneElement(child, {\n ...props,\n ref: mergeRefs(ref, child.props.ref),\n className: cn(child.props.className, className),\n 'aria-expanded': open,\n onClick: (e: MouseEvent<HTMLElement>) => {\n // Keep the child's own handler, then run the trigger's toggle — same\n // order as the previous nested-button version.\n child.props.onClick?.(e);\n handleTriggerClick(e);\n },\n } as Partial<HTMLAttributes<HTMLElement>> & { store?: DropdownStoreApi });\n }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n onClick={handleTriggerClick}\n aria-expanded={open}\n className={cn(\n 'appearance-none bg-transparent border-none p-0',\n className\n )}\n {...props}\n >\n {children}\n </button>\n );\n }\n);\nDropdownMenuTrigger.displayName = 'DropdownMenuTrigger';\n\nconst ITEM_SIZE_CLASSES = {\n small: 'text-sm',\n medium: 'text-md',\n} as const;\n\nconst SIDE_CLASSES = {\n top: 'bottom-full',\n right: 'top-full',\n bottom: 'top-full',\n left: 'top-full',\n};\n\nconst ALIGN_CLASSES = {\n start: 'left-0',\n center: 'left-1/2 -translate-x-1/2',\n end: 'right-0',\n};\n\n// O \"chrome\" do card (bg/borda/rounded/shadow) fica por variante para que a\n// variante `papole` possa ter o seu próprio (fundo secondary-500, sem borda,\n// sombra das vars --sds-* do Figma traduzidas: 0 8px 8px -2px, preto 10% + 20%).\n// `menu`/`profile` mantêm exatamente o visual de antes.\nconst MENUCONTENT_VARIANT_CLASSES = {\n menu: 'bg-background rounded-md border bg-popover text-popover-foreground shadow-md border-border-100 p-1',\n profile:\n 'bg-background rounded-md border bg-popover text-popover-foreground shadow-md border-border-100 p-6',\n papole:\n 'flex flex-col gap-4 bg-secondary-500 rounded-[20px] pt-6 px-6 pb-8 [box-shadow:0px_8px_8px_-2px_#0000001a,0px_8px_8px_-2px_#00000033]',\n};\n\nconst MenuLabel = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement> & {\n inset?: boolean;\n store?: DropdownStoreApi;\n }\n>(({ className, inset, store: _store, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={cn('text-sm w-full', inset ? 'pl-8' : '', className)}\n {...props}\n />\n );\n});\nMenuLabel.displayName = 'MenuLabel';\n\nconst DropdownMenuContent = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement> & {\n align?: 'start' | 'center' | 'end';\n side?: 'top' | 'right' | 'bottom' | 'left';\n variant?: 'menu' | 'profile' | 'papole';\n sideOffset?: number;\n store?: DropdownStoreApi;\n portal?: boolean;\n triggerRef?: RefObject<HTMLElement | null>;\n /**\n * Tallest the menu gets, in px, when the viewport allows it. Portaled menus\n * only — it is capped by the space actually available around the trigger.\n */\n maxHeight?: number;\n }\n>(\n (\n {\n className,\n align = 'start',\n side = 'bottom',\n variant = 'menu',\n sideOffset = 4,\n children,\n store: externalStore,\n portal = false,\n triggerRef,\n maxHeight = DEFAULT_PORTAL_MAX_HEIGHT,\n ...props\n },\n ref\n ) => {\n const store = useDropdownStore(externalStore);\n const open = useStore(store, (s) => s.open);\n const [isVisible, setIsVisible] = useState(open);\n const [portalStyle, setPortalStyle] = useState<CSSProperties>({});\n const contentRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (open) {\n setIsVisible(true);\n } else {\n const timer = setTimeout(() => setIsVisible(false), 200);\n return () => clearTimeout(timer);\n }\n }, [open]);\n\n const updatePortalPosition = useCallback(() => {\n if (!portal || !triggerRef?.current) return;\n\n const rect = triggerRef.current.getBoundingClientRect();\n const placement = { side, align, sideOffset, maxHeight };\n const style =\n side === 'left' || side === 'right'\n ? horizontalPortalStyle(rect, placement)\n : verticalPortalStyle(rect, placement);\n\n setPortalStyle((prev) => (isSamePortalStyle(prev, style) ? prev : style));\n }, [portal, triggerRef, align, side, sideOffset, maxHeight]);\n\n useLayoutEffect(() => {\n if (open) updatePortalPosition();\n }, [open, updatePortalPosition]);\n\n // A portaled menu is position:fixed, so it doesn't follow the trigger when\n // an ancestor scrolls. Tables wrap their body in `overflow-x-auto`, so a\n // menu anchored to a column header would drift away from its <th> on any\n // horizontal scroll. Capture-phase catches scrolls on any ancestor, not\n // just the window.\n useEffect(() => {\n if (!portal || !open) return;\n\n const reposition = () => updatePortalPosition();\n window.addEventListener('scroll', reposition, true);\n window.addEventListener('resize', reposition);\n\n return () => {\n window.removeEventListener('scroll', reposition, true);\n window.removeEventListener('resize', reposition);\n };\n }, [portal, open, updatePortalPosition]);\n\n if (!isVisible) return null;\n\n const getPositionClasses = () => {\n if (portal) {\n return 'fixed';\n }\n const vertical = SIDE_CLASSES[side];\n const horizontal = ALIGN_CLASSES[align];\n\n return `absolute ${vertical} ${horizontal}`;\n };\n\n const variantClasses = MENUCONTENT_VARIANT_CLASSES[variant];\n\n const content = (\n <div\n ref={portal ? contentRef : ref}\n role=\"menu\"\n data-dropdown-content=\"true\"\n data-open={open}\n className={`\n z-50 min-w-[210px] overflow-hidden\n ${open ? 'animate-in fade-in-0 zoom-in-95' : 'animate-out fade-out-0 zoom-out-95'}\n ${getPositionClasses()}\n ${variantClasses}\n ${className}\n `}\n style={{\n ...(portal\n ? portalStyle\n : {\n marginTop: side === 'bottom' ? sideOffset : undefined,\n marginBottom: side === 'top' ? sideOffset : undefined,\n marginLeft: side === 'right' ? sideOffset : undefined,\n marginRight: side === 'left' ? sideOffset : undefined,\n }),\n }}\n {...props}\n >\n {children}\n </div>\n );\n\n if (portal && typeof document !== 'undefined') {\n return createPortal(content, document.body);\n }\n\n return content;\n }\n);\nDropdownMenuContent.displayName = 'DropdownMenuContent';\n\nconst DropdownMenuItem = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement> & {\n inset?: boolean;\n size?: 'small' | 'medium';\n iconLeft?: ReactNode;\n iconRight?: ReactNode;\n disabled?: boolean;\n variant?: 'profile' | 'menu';\n store?: DropdownStoreApi;\n preventClose?: boolean;\n }\n>(\n (\n {\n className,\n size = 'small',\n children,\n iconRight,\n iconLeft,\n disabled = false,\n onClick,\n variant = 'menu',\n store: externalStore,\n preventClose = false,\n ...props\n },\n ref\n ) => {\n const store = useDropdownStore(externalStore);\n const setOpen = useStore(store, (s) => s.setOpen);\n const sizeClasses = ITEM_SIZE_CLASSES[size];\n\n const handleClick = (\n e: MouseEvent<HTMLDivElement> | KeyboardEvent<HTMLDivElement>\n ) => {\n if (disabled) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n if (e.type === 'click') {\n onClick?.(e as MouseEvent<HTMLDivElement>);\n } else if (e.type === 'keydown') {\n // For keyboard events, trigger click if Enter or Space was pressed\n const keyEvent = e as KeyboardEvent<HTMLDivElement>;\n if (keyEvent.key === 'Enter' || keyEvent.key === ' ') {\n (e.currentTarget as HTMLElement).click();\n }\n // honor any user-provided key handler\n props.onKeyDown?.(keyEvent);\n }\n if (!preventClose) {\n setOpen(false);\n }\n };\n\n const getVariantClasses = () => {\n if (variant === 'profile') {\n return 'relative flex flex-row justify-between select-none items-center gap-2 rounded-sm p-4 text-sm outline-none transition-colors [&>svg]:size-6 [&>svg]:shrink-0';\n }\n return 'relative flex select-none items-center gap-2 rounded-sm p-3 text-sm outline-none transition-colors [&>svg]:size-4 [&>svg]:shrink-0';\n };\n\n const getVariantProps = () => {\n return variant === 'profile' ? { 'data-variant': 'profile' } : {};\n };\n\n return (\n <div\n ref={ref}\n role=\"menuitem\"\n {...getVariantProps()}\n aria-disabled={disabled}\n className={`\n focus-visible:bg-background-50\n ${getVariantClasses()}\n ${sizeClasses}\n ${className}\n ${\n disabled\n ? 'cursor-not-allowed text-text-400'\n : 'cursor-pointer hover:bg-background-50 text-text-700 focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground'\n }\n `}\n onClick={handleClick}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n e.stopPropagation();\n handleClick(e);\n }\n }}\n tabIndex={disabled ? -1 : 0}\n {...props}\n >\n {iconLeft}\n <div className=\"w-full\">{children}</div>\n {iconRight}\n </div>\n );\n }\n);\nDropdownMenuItem.displayName = 'DropdownMenuItem';\n\nconst DropdownMenuSeparator = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement> & { store?: DropdownStoreApi }\n>(({ className, store: _store, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('my-1 h-px bg-border-200', className)}\n {...props}\n />\n));\nDropdownMenuSeparator.displayName = 'DropdownMenuSeparator';\n\n// Componentes específicos do ProfileMenu\nconst ProfileMenuTrigger = forwardRef<\n HTMLButtonElement,\n ButtonHTMLAttributes<HTMLButtonElement> & { store?: DropdownStoreApi }\n>(({ className, onClick, store: externalStore, ...props }, ref) => {\n const store = useDropdownStore(externalStore);\n const open = useStore(store, (s) => s.open);\n const toggleOpen = () => store.setState({ open: !open });\n\n return (\n <button\n ref={ref}\n className={cn(\n 'rounded-lg size-10 bg-primary-50 flex items-center justify-center cursor-pointer',\n className\n )}\n onClick={(e) => {\n e.stopPropagation();\n toggleOpen();\n onClick?.(e);\n }}\n aria-expanded={open}\n {...props}\n >\n <span className=\"size-6 rounded-full bg-primary-100 flex items-center justify-center\">\n <UserIcon className=\"text-primary-950\" size={18} />\n </span>\n </button>\n );\n});\nProfileMenuTrigger.displayName = 'ProfileMenuTrigger';\n\nconst ProfileMenuHeader = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement> & {\n name: string;\n email: string;\n photoUrl?: string | null;\n store?: DropdownStoreApi;\n }\n>(({ className, name, email, photoUrl, store: _store, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-component=\"ProfileMenuHeader\"\n className={cn(\n 'flex flex-row gap-4 items-center min-w-[280px]',\n className\n )}\n {...props}\n >\n <span className=\"w-16 h-16 bg-primary-100 rounded-full flex items-center justify-center overflow-hidden flex-shrink-0\">\n {photoUrl ? (\n <img\n src={photoUrl}\n alt=\"Foto de perfil\"\n className=\"w-full h-full object-cover\"\n />\n ) : (\n <UserIcon size={34} className=\"text-primary-800\" />\n )}\n </span>\n <div className=\"flex flex-col min-w-0\">\n <Text\n size=\"xl\"\n weight=\"bold\"\n color=\"text-text-950\"\n className=\"truncate\"\n >\n {name}\n </Text>\n <Text size=\"md\" color=\"text-text-600\" className=\"truncate\">\n {email}\n </Text>\n </div>\n </div>\n );\n});\nProfileMenuHeader.displayName = 'ProfileMenuHeader';\n\nconst ProfileMenuInfo = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement> & {\n schoolName: string;\n classYearName: string;\n schoolYearName: string;\n store?: DropdownStoreApi;\n }\n>(\n (\n {\n className,\n schoolName,\n classYearName,\n schoolYearName,\n store: _store,\n ...props\n },\n ref\n ) => {\n if (!schoolName && !classYearName && !schoolYearName) {\n return null;\n }\n\n return (\n <div\n ref={ref}\n data-component=\"ProfileMenuInfo\"\n className={cn('flex flex-row gap-4 items-center', className)}\n {...props}\n >\n <span className=\"w-16 h-16\" />\n <div className=\"flex flex-col \">\n {schoolName && (\n <Text size=\"md\" color=\"text-text-600\">\n {schoolName}\n </Text>\n )}\n\n {(classYearName || schoolYearName) && (\n <span className=\"flex flex-row items-center gap-2\">\n {classYearName && (\n <Text size=\"md\" color=\"text-text-600\">\n {classYearName}\n </Text>\n )}\n {classYearName && schoolYearName && (\n <Text size=\"md\" color=\"text-text-600\">\n ●\n </Text>\n )}\n {schoolYearName && (\n <Text size=\"md\" color=\"text-text-600\">\n {schoolYearName}\n </Text>\n )}\n </span>\n )}\n </div>\n </div>\n );\n }\n);\nProfileMenuInfo.displayName = 'ProfileMenuInfo';\n\nconst ProfileToggleTheme = ({\n store: externalStore,\n ...props\n}: HTMLAttributes<HTMLDivElement> & { store?: DropdownStoreApi }) => {\n const { themeMode, setTheme, isThemeLocked } = useTheme();\n const [modalThemeToggle, setModalThemeToggle] = useState(false);\n const [selectedTheme, setSelectedTheme] = useState<ThemeMode>(themeMode);\n\n const internalStoreRef = useRef<DropdownStoreApi | null>(null);\n internalStoreRef.current ??= createDropdownStore();\n const store = externalStore ?? internalStoreRef.current;\n const setOpen = useStore(store, (s) => s.setOpen);\n\n const handleClick = (e: MouseEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n setModalThemeToggle(true);\n };\n\n const handleSave = () => {\n setTheme(selectedTheme);\n setModalThemeToggle(false);\n setOpen(false); // Close dropdown after saving\n };\n\n const handleCancel = () => {\n setSelectedTheme(themeMode); // Reset to current theme\n setModalThemeToggle(false);\n setOpen(false); // Close dropdown after canceling\n };\n\n // Tema travado pelo produto (ex.: Fluência Leitora): esconde a entrada do\n // menu inteira. Deixá-la visível daria um modal que salva e não muda nada.\n if (isThemeLocked) return null;\n\n return (\n <>\n <DropdownMenuItem\n variant=\"profile\"\n preventClose={true}\n store={store}\n iconLeft={\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 25 25\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M12.5 2.75C15.085 2.75276 17.5637 3.78054 19.3916 5.6084C21.2195 7.43628 22.2473 9.915 22.25 12.5C22.25 14.4284 21.6778 16.3136 20.6064 17.917C19.5352 19.5201 18.0128 20.7699 16.2314 21.5078C14.4499 22.2458 12.489 22.4387 10.5977 22.0625C8.70642 21.6863 6.96899 20.758 5.60547 19.3945C4.24197 18.031 3.31374 16.2936 2.9375 14.4023C2.56129 12.511 2.75423 10.5501 3.49219 8.76855C4.23012 6.98718 5.47982 5.46483 7.08301 4.39355C8.68639 3.32221 10.5716 2.75 12.5 2.75ZM11.75 4.28516C9.70145 4.47452 7.7973 5.42115 6.41016 6.94043C5.02299 8.4599 4.25247 10.4426 4.25 12.5C4.25247 14.5574 5.02299 16.5401 6.41016 18.0596C7.7973 19.5789 9.70145 20.5255 11.75 20.7148V4.28516Z\"\n fill=\"currentColor\"\n />\n </svg>\n }\n iconRight={<CaretRightIcon />}\n onClick={handleClick}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n e.stopPropagation();\n setModalThemeToggle(true);\n }\n }}\n {...props}\n >\n <Text size=\"md\" color=\"text-text-700\">\n Aparência\n </Text>\n </DropdownMenuItem>\n\n <Modal\n isOpen={modalThemeToggle}\n onClose={handleCancel}\n title=\"Aparência\"\n size=\"md\"\n footer={\n <div className=\"flex gap-3\">\n <Button variant=\"outline\" onClick={handleCancel}>\n Cancelar\n </Button>\n <Button variant=\"solid\" onClick={handleSave}>\n Salvar\n </Button>\n </div>\n }\n >\n <div className=\"flex flex-col\">\n <p className=\"text-sm text-text-500\">Escolha o tema:</p>\n <ThemeToggle variant=\"with-save\" onToggle={setSelectedTheme} />\n </div>\n </Modal>\n </>\n );\n};\nProfileToggleTheme.displayName = 'ProfileToggleTheme';\n\nconst ProfileMenuSection = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement> & { store?: DropdownStoreApi }\n>(({ className, children, store: _store, ...props }, ref) => {\n return (\n <div ref={ref} className={cn('flex flex-col p-2', className)} {...props}>\n {children}\n </div>\n );\n});\nProfileMenuSection.displayName = 'ProfileMenuSection';\n\nconst ProfileMenuFooter = ({\n className,\n disabled = false,\n onClick,\n store: externalStore,\n ...props\n}: HTMLAttributes<HTMLButtonElement> & {\n disabled?: boolean;\n store?: DropdownStoreApi;\n}) => {\n const store = useDropdownStore(externalStore);\n const setOpen = useStore(store, (s) => s.setOpen);\n\n return (\n <Button\n variant=\"outline\"\n className={cn('w-full', className)}\n disabled={disabled}\n onClick={(e) => {\n setOpen(false);\n onClick?.(e);\n }}\n {...props}\n >\n <span className=\"mr-2 flex items-center\">\n <SignOutIcon className=\"text-inherit\" />\n </span>\n <Text color=\"inherit\">Sair</Text>\n </Button>\n );\n};\nProfileMenuFooter.displayName = 'ProfileMenuFooter';\n\n// ======================================================================\n// Componentes do ProfileMenu Reading Fluency (usados com variant=\"papole\")\n// ======================================================================\n\n// Trigger de perfil Reading Fluency: ícone circular de 42px. Mostra a foto do usuário\n// (`photoUrl`) e cai no passarinho quando não houver.\nconst ProfileMenuReadingFluencyTrigger = forwardRef<\n HTMLButtonElement,\n ButtonHTMLAttributes<HTMLButtonElement> & {\n photoUrl?: string | null;\n store?: DropdownStoreApi;\n }\n>(({ className, onClick, photoUrl, store: externalStore, ...props }, ref) => {\n const store = useDropdownStore(externalStore);\n const open = useStore(store, (s) => s.open);\n const toggleOpen = () => store.setState({ open: !open });\n\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn(\n 'size-[42px] rounded-full overflow-hidden cursor-pointer bg-secondary-600 flex items-center justify-center',\n className\n )}\n onClick={(e) => {\n e.stopPropagation();\n toggleOpen();\n onClick?.(e);\n }}\n aria-expanded={open}\n aria-label=\"Abrir menu de perfil\"\n {...props}\n >\n <img\n src={photoUrl ?? readingFluencyBird}\n alt=\"Foto de perfil\"\n className=\"w-full h-full object-cover\"\n />\n </button>\n );\n});\nProfileMenuReadingFluencyTrigger.displayName =\n 'ProfileMenuReadingFluencyTrigger';\n\n// Card de informações do aluno (fundo secondary-600). Dois blocos texto1/texto2:\n// nome/email (sempre) e escola/turma (quando houver).\nconst ProfileMenuReadingFluencyInfo = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement> & {\n name: string;\n email: string;\n schoolName?: string;\n classYearName?: string;\n schoolYearName?: string;\n store?: DropdownStoreApi;\n }\n>(\n (\n {\n className,\n name,\n email,\n schoolName,\n classYearName,\n schoolYearName,\n store: _store,\n ...props\n },\n ref\n ) => {\n const hasSchoolBlock = !!(schoolName || classYearName || schoolYearName);\n\n return (\n <div\n ref={ref}\n data-component=\"ProfileMenuReadingFluencyInfo\"\n className={cn(\n 'font-quicksand flex flex-col gap-4 rounded-[20px] bg-secondary-600 p-4',\n className\n )}\n {...props}\n >\n <div className=\"flex flex-col\">\n <Text size=\"md\" className=\"font-bold text-text-100 truncate\">\n {name}\n </Text>\n <Text size=\"sm\" className=\"font-medium text-secondary-300 truncate\">\n {email}\n </Text>\n </div>\n\n {hasSchoolBlock && (\n <div className=\"flex flex-col\">\n {schoolName && (\n <Text size=\"md\" className=\"font-bold text-text-100 truncate\">\n {schoolName}\n </Text>\n )}\n {(classYearName || schoolYearName) && (\n <Text\n size=\"sm\"\n className=\"font-medium text-secondary-300 truncate\"\n >\n {[classYearName, schoolYearName].filter(Boolean).join(' • ')}\n </Text>\n )}\n </div>\n )}\n </div>\n );\n }\n);\nProfileMenuReadingFluencyInfo.displayName = 'ProfileMenuReadingFluencyInfo';\n\n// Rodapé Reading Fluency: botão \"Sair\" (ButtonReadingFluency outline md). Fecha o menu e repassa\n// o onClick (logout).\nconst ProfileMenuReadingFluencyFooter = ({\n className,\n disabled = false,\n onClick,\n store: externalStore,\n ...props\n}: ButtonHTMLAttributes<HTMLButtonElement> & {\n disabled?: boolean;\n store?: DropdownStoreApi;\n}) => {\n const store = useDropdownStore(externalStore);\n const setOpen = useStore(store, (s) => s.setOpen);\n\n return (\n <ButtonReadingFluency\n variant=\"outline-inverse\"\n size=\"medium\"\n className={cn('w-full', className)}\n disabled={disabled}\n onClick={(e) => {\n setOpen(false);\n onClick?.(e);\n }}\n {...props}\n >\n Sair\n </ButtonReadingFluency>\n );\n};\nProfileMenuReadingFluencyFooter.displayName = 'ProfileMenuReadingFluencyFooter';\n\n// Exportações\nexport default DropdownMenu;\nexport {\n // Componentes genéricos\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n MenuLabel,\n DropdownMenuSeparator,\n\n // Componentes específicos do ProfileMenu\n ProfileMenuTrigger,\n ProfileMenuHeader,\n ProfileMenuSection,\n ProfileMenuFooter,\n ProfileToggleTheme,\n ProfileMenuInfo,\n\n // Componentes do ProfileMenu Reading Fluency\n ProfileMenuReadingFluencyTrigger,\n ProfileMenuReadingFluencyInfo,\n ProfileMenuReadingFluencyFooter,\n};\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-OMNDC4BZ.js","../src/components/SimulationsPage/SimulationsDetailModal.tsx","../src/components/shared/SimulationSummaryCards.tsx","../src/utils/simulatedScore.ts","../src/utils/questionDuration.ts","../src/hooks/useSimulationCardDetails.ts"],"names":["jsx","jsxs"],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-OMNDC4BZ.js","sourcesContent":[null,"import { useEffect, useMemo, useRef, useState } from 'react';\nimport type { ChangeEvent } from 'react';\nimport { PaperclipIcon } from '@phosphor-icons/react/dist/csr/Paperclip';\nimport { XIcon } from '@phosphor-icons/react/dist/csr/X';\nimport Modal from '../Modal/Modal';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport TextArea from '../TextArea/TextArea';\nimport { CardAccordation } from '../Accordation';\nimport { SkeletonCard } from '../Skeleton/Skeleton';\nimport { QuestionCommentField } from '../shared/QuestionCommentField';\nimport {\n TrueFalseStatementList,\n type TrueFalseStatement,\n} from '../shared/TrueFalseStatementList';\nimport { ImageAnswerView } from '../shared/ImageAnswerView';\nimport { DEFAULT_IMAGE_TOLERANCE } from '../../utils/image/imageAnswer.utils';\nimport { AlternativesList } from '../Alternative/Alternative';\nimport { HtmlMathRenderer } from '../HtmlMathRenderer';\nimport { OptionStatus } from '../../enums/Options';\nimport { QUESTION_TYPE } from '../Quiz/useQuizStore';\nimport {\n getQuestionStatusBadgeConfig,\n QUESTION_STATUS,\n type QuestionStatus,\n} from '../../utils/studentActivityCorrection';\nimport {\n buildSimulationMeta,\n ContentCards,\n DataCard,\n SectionTitle,\n SimulationCardShell,\n SimulationStatCards,\n StudentSummaryHeader,\n} from '../shared/SimulationSummaryCards';\nimport { cn } from '../../utils/utils';\nimport { formatQuestionDuration } from '../../utils/questionDuration';\nimport { formatTimeSpent } from '../../utils/activityDetailsUtils';\nimport type { BaseApiClient } from '../../types/api';\nimport { createUseSimulations } from '../../hooks/useSimulations';\nimport {\n useSimulationCardDetails,\n type SimulationCardHandlers,\n type SimulationDetailState,\n type SimulationNoteState,\n} from '../../hooks/useSimulationCardDetails';\nimport type {\n SimulationsListData,\n SimulationDetailQuestion,\n StudentSimulationItem,\n NoteData,\n} from '../../types/simulations';\n\nexport interface SimulationsDetailModalProps {\n /** API client used to fetch the student's simulations */\n readonly api: BaseApiClient;\n readonly isOpen: boolean;\n readonly onClose: () => void;\n /** The student whose simulations are shown (null closes the modal) */\n readonly student: { userInstitutionId: string; name: string } | null;\n}\n\n/** Map the simulation question status to the shared correction status. */\nconst QUESTION_STATUS_MAP: Record<\n SimulationDetailQuestion['status'],\n QuestionStatus\n> = {\n CORRECT: QUESTION_STATUS.CORRETA,\n INCORRECT: QUESTION_STATUS.INCORRETA,\n BLANK: QUESTION_STATUS.EM_BRANCO,\n PENDING: QUESTION_STATUS.PENDENTE,\n};\n\n/** Label of the inner accordion holding the student's answer. */\nfunction getAnswerAccordionTitle(questionType: string): string {\n if (questionType === QUESTION_TYPE.DISSERTATIVA) {\n return 'Resposta do aluno';\n }\n if (questionType === QUESTION_TYPE.VERDADEIRO_FALSO) {\n return 'Afirmações';\n }\n if (questionType === QUESTION_TYPE.IMAGEM) {\n return 'Imagem';\n }\n return 'Alternativas';\n}\n\n// ---------------------------------------------------------------------------\n// Level 2 — Question (reuses the shared alternatives renderer + status badge)\n// ---------------------------------------------------------------------------\n\nexport interface SimulationQuestionItemProps {\n readonly question: SimulationDetailQuestion;\n /** Zero-based position in the simulation; shown as \"Questão N\". */\n readonly index: number;\n /** Persist the teacher comment on this question; an empty string clears it. */\n readonly onSaveComment: (comment: string) => Promise<void>;\n}\n\n/**\n * One question of a student's simulation: an accordion with the status badge\n * in its header and, inside, the statement, the student's answer (alternatives,\n * true/false marks, essay text or image click, by question type) and the\n * teacher comment field.\n *\n * Exported so consumers that list a student's simulations in their own layout\n * (the Desempenho report of the teacher app) render questions exactly as the\n * Simulados page does.\n */\nexport function SimulationQuestionItem({\n question,\n index,\n onSaveComment,\n}: SimulationQuestionItemProps) {\n const badge = getQuestionStatusBadgeConfig(\n QUESTION_STATUS_MAP[question.status]\n );\n\n // Subject and duration only join the label when they exist: a question with\n // no subject in the knowledge matrix and a simulation answered before\n // per-question telemetry both fall back to a plain \"Questão N\".\n const label = [\n `Questão ${index + 1}`,\n question.subject,\n formatQuestionDuration(question.timeSpent),\n ]\n .filter(Boolean)\n .join(' - ');\n\n const alternatives = question.options.map((option) => {\n let status: OptionStatus;\n if (option.isCorrect) {\n status = OptionStatus.CORRECT;\n } else if (option.isSelected) {\n status = OptionStatus.INCORRECT;\n } else {\n status = OptionStatus.NEUTRAL;\n }\n return { label: option.option, value: option.id, status };\n });\n\n // An essay has no alternatives to show — it has the text the student wrote.\n // Rendering the \"Alternativas\" accordion for it produced an empty box and hid\n // the answer entirely, leaving the teacher to comment on nothing.\n const isEssay = question.questionType === QUESTION_TYPE.DISSERTATIVA;\n\n // True/false never writes `option_id`, so `isSelected` is always false and the\n // student's marks live in each option's `selectedValue`. Sending it through\n // the alternatives branch showed the answer key as if it were the student's\n // answer, and no mark at all.\n const isTrueFalse = question.questionType === QUESTION_TYPE.VERDADEIRO_FALSO;\n const trueFalseStatements: TrueFalseStatement[] = question.options.map(\n (option) => ({\n id: option.id,\n statement: option.option,\n studentMark: option.selectedValue ?? null,\n isTrue: option.isCorrect,\n })\n );\n\n // IMAGEM has no alternatives either: the answer is a point on an image. The\n // alternatives branch rendered the answer key's raw JSON as an option label,\n // painted green, and never showed where the student clicked.\n const isImage = question.questionType === QUESTION_TYPE.IMAGEM;\n\n /**\n * Render the student's answer, shaped by the question type.\n */\n const renderAnswerArea = () => {\n if (isImage) {\n return (\n <ImageAnswerView\n imageUrl={question.additionalContent ?? ''}\n correctPoint={question.correctPoint}\n studentPoint={\n question.imageAnswer\n ? {\n x: question.imageAnswer.coordinateX,\n y: question.imageAnswer.coordinateY,\n }\n : null\n }\n toleranceRadius={question.imageTolerance ?? DEFAULT_IMAGE_TOLERANCE}\n />\n );\n }\n\n if (isEssay) {\n return (\n <div className=\"rounded-lg border border-border-100 bg-background-50 p-3\">\n {question.answer ? (\n <HtmlMathRenderer\n content={question.answer}\n className=\"text-sm text-text-800\"\n />\n ) : (\n <Text size=\"sm\" className=\"text-text-600\">\n Nenhuma resposta fornecida\n </Text>\n )}\n </div>\n );\n }\n\n if (isTrueFalse) {\n return <TrueFalseStatementList statements={trueFalseStatements} />;\n }\n\n return (\n <AlternativesList\n mode=\"readonly\"\n layout=\"compact\"\n name={`question-${question.questionId}`}\n alternatives={alternatives}\n selectedValue={question.selectedOptionId ?? ''}\n />\n );\n };\n\n return (\n <CardAccordation\n value={question.questionId}\n trigger={\n <div className=\"flex flex-1 items-center justify-between gap-3 py-3\">\n <Text size=\"sm\" weight=\"bold\" className=\"min-w-0 text-text-950\">\n {label}\n </Text>\n <span\n className={cn(\n 'inline-flex shrink-0 items-center rounded-full px-2 py-0.5 text-xs font-medium',\n badge.bgColor,\n badge.textColor\n )}\n >\n {badge.label}\n </span>\n </div>\n }\n contentClassName=\"px-3 pb-3\"\n >\n <div className=\"flex flex-col gap-3\">\n <HtmlMathRenderer\n content={question.statement}\n className=\"text-sm text-text-800\"\n />\n <CardAccordation\n value={`${question.questionId}-answer`}\n trigger={\n <div className=\"flex-1 py-2\">\n <Text size=\"sm\" weight=\"medium\" className=\"text-text-950\">\n {getAnswerAccordionTitle(question.questionType)}\n </Text>\n </div>\n }\n contentClassName=\"px-3 pb-3\"\n >\n {renderAnswerArea()}\n </CardAccordation>\n <QuestionCommentField\n value={question.teacherComment ?? ''}\n onSave={onSaveComment}\n />\n </div>\n </CardAccordation>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Note (\"Observação\")\n// ---------------------------------------------------------------------------\n\nexport interface SimulationNoteRowProps {\n /** Current observation, null when the teacher never wrote one. */\n readonly note: NoteData | null;\n readonly loading: boolean;\n /**\n * Message of a failed load. While it is set the row offers a retry instead\n * of the editor: writing over a note that could not be read would replace\n * it, and this save has no version to protect it.\n */\n readonly loadError?: string | null;\n /** Load the observation again; required whenever `loadError` can be set. */\n readonly onRetry?: () => void;\n /**\n * Persist the observation. `text` is already trimmed and non-empty; `file`\n * is a newly chosen attachment still to be uploaded (null when none), and\n * `existingAttachment` is the URL of the saved file the teacher kept — null\n * when there was none, when it was removed, or when a new file replaces it.\n */\n readonly onSave: (\n text: string,\n file: File | null,\n existingAttachment: string | null\n ) => Promise<void>;\n}\n\n/**\n * Human label of an attachment URL: its file name, or a generic word when the\n * URL carries none.\n */\nfunction getAttachmentLabel(url: string): string {\n const lastSegment = url.split('?')[0].split('/').pop() ?? '';\n try {\n return decodeURIComponent(lastSegment) || 'Anexo';\n } catch {\n return lastSegment || 'Anexo';\n }\n}\n\n/**\n * Grey pill naming an attached file. Links to the file when `href` is given\n * and offers a remove button when `onRemove` is given — the same chip the\n * activity correction modal uses for its observation attachment.\n */\nfunction AttachmentChip({\n label,\n href,\n onRemove,\n}: {\n readonly label: string;\n readonly href?: string;\n readonly onRemove?: () => void;\n}) {\n const content = (\n <>\n <PaperclipIcon size={18} className=\"shrink-0 text-text-800\" />\n <span className=\"truncate text-md font-medium text-text-800\">\n {label}\n </span>\n </>\n );\n\n return (\n <div className=\"flex h-10 min-w-0 max-w-[220px] items-center gap-2 rounded-full bg-secondary-500 px-5\">\n {href ? (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex min-w-0 items-center gap-2 hover:underline\"\n >\n {content}\n </a>\n ) : (\n content\n )}\n {onRemove && (\n <button\n type=\"button\"\n onClick={onRemove}\n className=\"shrink-0 text-text-700 hover:text-text-950\"\n aria-label={`Remover ${label}`}\n >\n <XIcon size={18} />\n </button>\n )}\n </div>\n );\n}\n\n/**\n * The simulation-wide teacher observation: a row with the saved text, the\n * attached file (if any) and an \"Incluir\"/\"Editar\" button that swaps into a\n * textarea with \"Anexar\" and \"Salvar\" — the same flow as the observation of\n * the activity correction modal.\n *\n * Exported for the same reason as {@link SimulationQuestionItem}.\n */\nexport function SimulationNoteRow({\n note,\n loading,\n loadError,\n onRetry,\n onSave,\n}: SimulationNoteRowProps) {\n const [editing, setEditing] = useState(false);\n const [draft, setDraft] = useState('');\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n /** File chosen in this editing session, not uploaded yet. */\n const [pendingFile, setPendingFile] = useState<File | null>(null);\n /** Saved attachment the teacher is keeping; null once removed. */\n const [keptAttachment, setKeptAttachment] = useState<string | null>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const savedAttachment = note?.attachment ?? null;\n\n const startEditing = () => {\n setDraft(note?.note ?? '');\n setPendingFile(null);\n setKeptAttachment(savedAttachment);\n setError(null);\n setEditing(true);\n };\n\n const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0] ?? null;\n if (file) setPendingFile(file);\n // Reset so the same file can be picked again after being removed.\n event.target.value = '';\n };\n\n const handleSave = async () => {\n if (!draft.trim()) return;\n setSaving(true);\n setError(null);\n try {\n await onSave(\n draft.trim(),\n pendingFile,\n pendingFile ? null : keptAttachment\n );\n setEditing(false);\n } catch {\n // Keep the editing UI open (draft preserved) and surface the failure.\n setError('Erro ao salvar a observação. Tente novamente.');\n } finally {\n setSaving(false);\n }\n };\n\n /** Left side of the footer: the chosen/kept file, or the \"Anexar\" button. */\n const renderAttachmentControl = () => {\n if (pendingFile) {\n return (\n <AttachmentChip\n label={pendingFile.name}\n onRemove={() => setPendingFile(null)}\n />\n );\n }\n if (keptAttachment) {\n return (\n <AttachmentChip\n label={getAttachmentLabel(keptAttachment)}\n href={keptAttachment}\n onRemove={() => setKeptAttachment(null)}\n />\n );\n }\n return (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"medium\"\n onClick={() => fileInputRef.current?.click()}\n className=\"flex items-center gap-2\"\n >\n <PaperclipIcon size={18} />\n Anexar\n </Button>\n );\n };\n\n if (loading) {\n return <SkeletonCard className=\"h-14\" />;\n }\n\n if (loadError) {\n return (\n <div className=\"flex items-center justify-between gap-2 rounded-lg border border-border-200 bg-background p-4\">\n <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n <Text size=\"md\" weight=\"bold\" className=\"text-text-950\">\n Observação\n </Text>\n <Text size=\"sm\" className=\"text-error-600\">\n {loadError}\n </Text>\n </div>\n {onRetry && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"medium\"\n onClick={onRetry}\n className=\"shrink-0\"\n >\n Tentar novamente\n </Button>\n )}\n </div>\n );\n }\n\n if (editing) {\n return (\n <div className=\"flex flex-col gap-4 rounded-lg border border-border-200 bg-background p-4\">\n <Text size=\"md\" weight=\"bold\" className=\"text-text-950\">\n Observação\n </Text>\n <TextArea\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n placeholder=\"Escreva uma observação para este simulado\"\n rows={3}\n />\n {error && (\n <Text size=\"sm\" className=\"text-error-600\">\n {error}\n </Text>\n )}\n {/* Only images: the pre-signed upload endpoint accepts nothing else. */}\n <input\n type=\"file\"\n ref={fileInputRef}\n className=\"hidden\"\n accept=\"image/*\"\n onChange={handleFileChange}\n aria-label=\"Selecionar arquivo\"\n />\n <div className=\"flex flex-col-reverse gap-3 sm:flex-row sm:items-center sm:justify-between\">\n {renderAttachmentControl()}\n <Button\n type=\"button\"\n variant=\"solid\"\n size=\"medium\"\n onClick={handleSave}\n disabled={saving || !draft.trim()}\n >\n Salvar\n </Button>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex items-center justify-between gap-2 rounded-lg border border-border-200 bg-background p-4\">\n <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n <Text size=\"md\" weight=\"bold\" className=\"text-text-950\">\n Observação\n </Text>\n {note?.note && (\n <Text size=\"sm\" className=\"truncate text-text-700\">\n {note.note}\n </Text>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {savedAttachment && (\n <AttachmentChip\n label={getAttachmentLabel(savedAttachment)}\n href={savedAttachment}\n />\n )}\n <Button\n type=\"button\"\n variant=\"solid\"\n size=\"medium\"\n onClick={startEditing}\n >\n {note?.note ? 'Editar' : 'Incluir'}\n </Button>\n </div>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Level 1 — Simulation\n// ---------------------------------------------------------------------------\n\n/**\n * Observation and question list of one simulado, loaded when its card is\n * first expanded.\n */\nexport function SimulationAnswers({\n detail,\n note,\n onRetryNote,\n onSaveNote,\n onSaveQuestionComment,\n}: {\n readonly detail: SimulationDetailState | undefined;\n readonly note: SimulationNoteState | undefined;\n /** Load the observation again after a failed request */\n readonly onRetryNote: () => void;\n readonly onSaveNote: SimulationNoteRowProps['onSave'];\n readonly onSaveQuestionComment: (\n questionId: string,\n comment: string\n ) => Promise<void>;\n}) {\n if (!detail || detail.loading) {\n return <SkeletonCard className=\"h-40\" />;\n }\n\n if (detail.error) {\n return (\n <Text size=\"sm\" className=\"text-error-600\">\n {detail.error}\n </Text>\n );\n }\n\n if (!detail.data) return null;\n\n return (\n <>\n <SimulationNoteRow\n note={note?.data ?? null}\n loading={note?.loading ?? false}\n loadError={note?.error ?? null}\n onRetry={onRetryNote}\n onSave={onSaveNote}\n />\n\n <div className=\"flex flex-col gap-2 pt-2\">\n <Text as=\"h4\" size=\"lg\" weight=\"bold\" className=\"text-text-950\">\n Respostas\n </Text>\n {detail.data.questions.map((question, qIndex) => (\n <SimulationQuestionItem\n key={question.questionId}\n question={question}\n index={qIndex}\n onSaveComment={(comment) =>\n onSaveQuestionComment(question.questionId, comment)\n }\n />\n ))}\n </div>\n </>\n );\n}\n\nfunction SimulationItem({\n simulation,\n index,\n expanded,\n onToggle,\n detail,\n note,\n onRetryNote,\n onSaveNote,\n onSaveQuestionComment,\n}: SimulationCardHandlers & {\n readonly simulation: StudentSimulationItem;\n readonly index: number;\n readonly expanded: boolean;\n readonly onToggle: () => void;\n}) {\n const title = simulation.title?.trim()\n ? simulation.title.trim()\n : `Simulado ${index + 1}`;\n const meta = buildSimulationMeta(simulation);\n\n return (\n <SimulationCardShell\n value={simulation.id}\n title={title}\n meta={meta}\n correct={simulation.correctCount}\n totalQuestions={simulation.totalQuestions}\n expanded={expanded}\n onToggle={onToggle}\n >\n <SimulationStatCards\n score={simulation.score}\n correct={simulation.correctCount}\n incorrect={simulation.incorrectCount}\n blank={simulation.blankCount}\n />\n {/* Essays awaiting grading used to be counted as blank. */}\n {detail?.data && detail.data.counts.pending > 0 && (\n <Text size=\"sm\" className=\"text-text-600\">\n {`${detail.data.counts.pending} ${detail.data.counts.pending === 1 ? 'questão dissertativa aguarda' : 'questões dissertativas aguardam'} correção`}\n </Text>\n )}\n <ContentCards\n best={simulation.bestContent ?? null}\n worst={simulation.worstContent ?? null}\n />\n {expanded && (\n <SimulationAnswers\n detail={detail}\n note={note}\n onRetryNote={onRetryNote}\n onSaveNote={onSaveNote}\n onSaveQuestionComment={onSaveQuestionComment}\n />\n )}\n </SimulationCardShell>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Modal\n// ---------------------------------------------------------------------------\n\n/**\n * Modal that shows a single student's answered simulations as a nested\n * accordion (simulation -> question -> options), lazily loading each\n * simulation's detail when it is expanded. Reuses the shared StatCard and\n * AlternativesList components so the detail matches the activity correction UI.\n */\nexport function SimulationsDetailModal({\n api,\n isOpen,\n onClose,\n student,\n}: SimulationsDetailModalProps) {\n const useSimulations = useMemo(() => createUseSimulations(api), [api]);\n const { fetchStudentSimulations } = useSimulations();\n\n const [list, setList] = useState<SimulationsListData | null>(null);\n const [listLoading, setListLoading] = useState(false);\n const [listError, setListError] = useState<string | null>(null);\n\n const {\n expandedId,\n details,\n notes,\n toggle,\n retryNote,\n makeSaveNote,\n makeSaveQuestionComment,\n } = useSimulationCardDetails({\n api,\n userInstitutionId: student?.userInstitutionId ?? null,\n isOpen,\n });\n\n useEffect(() => {\n if (!isOpen || !student) return;\n\n setList(null);\n setListLoading(true);\n setListError(null);\n\n let active = true;\n fetchStudentSimulations(student.userInstitutionId)\n .then((data) => {\n if (active) setList(data);\n })\n .catch(() => {\n if (active) setListError('Erro ao carregar os simulados do estudante');\n })\n .finally(() => {\n if (active) setListLoading(false);\n });\n\n return () => {\n active = false;\n };\n }, [isOpen, student, fetchStudentSimulations]);\n\n return (\n <Modal isOpen={isOpen} onClose={onClose} title=\"Simulados\" size=\"xl\">\n {student && (\n <div className=\"flex max-h-[70vh] flex-col gap-6 overflow-y-auto pr-1\">\n <StudentSummaryHeader\n name={student.name}\n location={[\n list?.student.school,\n list?.student.class,\n list?.student.schoolYear,\n ]}\n />\n\n {listLoading && (\n <>\n <SkeletonCard className=\"h-20\" />\n <SkeletonCard className=\"h-40\" />\n </>\n )}\n {listError && (\n <Text size=\"sm\" className=\"text-error-600\">\n {listError}\n </Text>\n )}\n\n {list && (\n <section className=\"flex flex-col gap-3\">\n <SectionTitle>Dados de simulados</SectionTitle>\n <div className=\"flex flex-col gap-2 md:flex-row\">\n <DataCard\n label=\"Simulados realizados\"\n value={String(list.student.simulationsAnswered)}\n />\n {list.student.totalTimeSeconds !== undefined && (\n <DataCard\n label=\"Tempo total\"\n value={formatTimeSpent(list.student.totalTimeSeconds)}\n />\n )}\n </div>\n </section>\n )}\n\n {list && (\n <section className=\"flex flex-col gap-3\">\n <SectionTitle>Simulados realizados</SectionTitle>\n {list.simulations.data.length === 0 ? (\n <div className=\"flex items-center justify-center rounded-xl border border-border-50 bg-background p-6\">\n <Text size=\"sm\" className=\"text-text-600\">\n Este estudante ainda não respondeu nenhum simulado.\n </Text>\n </div>\n ) : (\n <div className=\"flex flex-col gap-2\">\n {list.simulations.data.map((simulation, index) => (\n <SimulationItem\n // Keyed by the student too: the comment fields below keep\n // a dirty draft through a `value` change so an in-flight\n // save cannot discard it, and a note written for one\n // student must never survive into another. Remounting\n // resets it for free.\n key={`${student.userInstitutionId}-${simulation.id}`}\n simulation={simulation}\n index={index}\n expanded={expandedId === simulation.id}\n onToggle={() => toggle(simulation.id)}\n detail={details[simulation.id]}\n note={notes[simulation.id]}\n onRetryNote={() => retryNote(simulation.id)}\n onSaveNote={makeSaveNote(simulation.id)}\n onSaveQuestionComment={makeSaveQuestionComment(\n simulation.id\n )}\n />\n ))}\n </div>\n )}\n </section>\n )}\n </div>\n )}\n </Modal>\n );\n}\n","import type { ReactNode } from 'react';\nimport { ExamIcon } from '@phosphor-icons/react/dist/csr/Exam';\nimport { CheckCircleIcon } from '@phosphor-icons/react/dist/csr/CheckCircle';\nimport { XCircleIcon } from '@phosphor-icons/react/dist/csr/XCircle';\nimport { MinusCircleIcon } from '@phosphor-icons/react/dist/csr/MinusCircle';\nimport Text from '../Text/Text';\nimport ProgressBar from '../ProgressBar/ProgressBar';\nimport { CardAccordation } from '../Accordation';\nimport { UserIcon } from '../UserIcon/UserIcon';\nimport { cn } from '../../utils/utils';\nimport { formatScoreOutOfTen } from '../../utils/simulatedScore';\nimport {\n formatDateToBrazilian,\n formatTimeSpent,\n} from '../../utils/activityDetailsUtils';\n\n/**\n * Summary blocks of a student's simulados: header, \"Dados de simulados\" cards,\n * the coloured stat cards and the best/worst subtema pair.\n *\n * Shared by the two modals that show them — the Simulados page modal and the\n * Simulados report modal — so the design is described once. Everything here is\n * presentational: it takes already-fetched numbers and returns markup.\n */\n\n/**\n * The only field these cards read from a subtema, so both the Simulados page\n * shape and the report shape satisfy it.\n */\nexport interface SimulationContentSummary {\n readonly contentName: string;\n}\n\n/** The fields the \"Duração · Nota · Feito em\" line reads from a simulado. */\nexport interface SimulationMetaSource {\n readonly timeSpentSeconds?: number;\n readonly score?: number;\n readonly answeredAt?: string | null;\n}\n\n/**\n * \"Duração · Nota · Feito em\" line of a simulado card. Each segment only joins\n * when its field came in the payload, so an older backend shows a shorter line\n * instead of \"undefined\".\n */\nexport function buildSimulationMeta(simulation: SimulationMetaSource): string {\n const parts: string[] = [];\n if (simulation.timeSpentSeconds !== undefined) {\n parts.push(`Duração: ${formatTimeSpent(simulation.timeSpentSeconds)}`);\n }\n if (simulation.score !== undefined) {\n parts.push(`Nota: ${formatScoreOutOfTen(simulation.score)}`);\n }\n if (simulation.answeredAt) {\n parts.push(`Feito em: ${formatDateToBrazilian(simulation.answeredAt)}`);\n }\n return parts.join(' · ');\n}\n\n/** Section heading, \"Dados de simulados\" and its siblings (14px bold). */\nexport function SectionTitle({ children }: { readonly children: ReactNode }) {\n return (\n <Text as=\"h3\" size=\"sm\" weight=\"bold\" className=\"text-text-950\">\n {children}\n </Text>\n );\n}\n\n/**\n * White card with a centred uppercase label and the value in a rectangular\n * info badge: the \"Dados de simulados\" pair.\n */\nexport function DataCard({\n label,\n value,\n}: {\n readonly label: string;\n readonly value: string;\n}) {\n return (\n <div className=\"flex flex-1 flex-col items-center gap-2 rounded-xl border border-border-50 bg-background px-3 py-4\">\n <Text\n size=\"2xs\"\n weight=\"medium\"\n className=\"text-center uppercase text-text-800\"\n >\n {label}\n </Text>\n <span className=\"rounded-sm bg-info-background px-2 py-1 text-sm text-info-800\">\n {value}\n </span>\n </div>\n );\n}\n\n/** Tone of a coloured stat card; each maps to one token family. */\ntype StatTone = 'grade' | 'correct' | 'incorrect' | 'blank';\n\nconst STAT_TONE_CLASSES: Record<\n StatTone,\n { card: string; circle: string; icon: string; value: string }\n> = {\n grade: {\n card: 'bg-warning-background',\n circle: 'bg-warning-300',\n icon: 'text-text',\n value: 'text-warning-600',\n },\n correct: {\n card: 'bg-success-200',\n circle: 'bg-indicator-positive',\n icon: 'text-text-950',\n value: 'text-success-700',\n },\n incorrect: {\n card: 'bg-error-100',\n circle: 'bg-error-500',\n icon: 'text-text',\n value: 'text-error-700',\n },\n blank: {\n card: 'bg-info-background',\n circle: 'bg-info-500',\n icon: 'text-text',\n value: 'text-info-700',\n },\n};\n\n/**\n * Coloured stat card, laid out as a centred column: icon in a circle, tiny\n * uppercase label and the large value.\n */\nfunction SimulationStatCard({\n tone,\n icon,\n label,\n value,\n}: {\n readonly tone: StatTone;\n readonly icon: ReactNode;\n readonly label: string;\n readonly value: string;\n}) {\n const classes = STAT_TONE_CLASSES[tone];\n return (\n <div\n className={cn(\n 'flex flex-1 flex-col items-center gap-1 rounded-xl border border-border-50 px-3 py-4',\n classes.card\n )}\n >\n <span\n className={cn(\n 'flex size-8 shrink-0 items-center justify-center rounded-full',\n classes.circle,\n classes.icon\n )}\n >\n {icon}\n </span>\n <Text\n as=\"span\"\n weight=\"bold\"\n className=\"text-center text-[8px] leading-3 uppercase text-text-800\"\n >\n {label}\n </Text>\n <Text\n size=\"xl\"\n weight=\"bold\"\n className={cn('text-center', classes.value)}\n >\n {value}\n </Text>\n </div>\n );\n}\n\n/**\n * The four cards of a cut: grade, correct, incorrect and blank. The grade card\n * is skipped when no score was sent, so an older API still renders the three\n * counts it always had.\n */\nexport function SimulationStatCards({\n score,\n correct,\n incorrect,\n blank,\n}: {\n readonly score: number | undefined;\n readonly correct: number;\n readonly incorrect: number;\n readonly blank: number;\n}) {\n return (\n <div className=\"grid grid-cols-2 gap-2 lg:grid-cols-4\">\n {score !== undefined && (\n <SimulationStatCard\n tone=\"grade\"\n icon={<ExamIcon size={16} weight=\"bold\" />}\n label=\"Nota média\"\n value={formatScoreOutOfTen(score)}\n />\n )}\n <SimulationStatCard\n tone=\"correct\"\n icon={<CheckCircleIcon size={16} weight=\"bold\" />}\n label=\"Nº de questões corretas\"\n value={String(correct)}\n />\n <SimulationStatCard\n tone=\"incorrect\"\n icon={<XCircleIcon size={16} weight=\"bold\" />}\n label=\"Nº de questões incorretas\"\n value={String(incorrect)}\n />\n <SimulationStatCard\n tone=\"blank\"\n icon={<MinusCircleIcon size={16} weight=\"bold\" />}\n label=\"Nº de questões em branco\"\n value={String(blank)}\n />\n </div>\n );\n}\n\n/** One subtema card: centred coloured label over the content name. */\nfunction ContentCard({\n label,\n labelClassName,\n content,\n}: {\n readonly label: string;\n readonly labelClassName: string;\n readonly content: SimulationContentSummary | null;\n}) {\n return (\n <div className=\"flex flex-1 flex-col items-center gap-2 rounded-xl border border-border-50 bg-background p-4\">\n <Text\n size=\"2xs\"\n weight=\"medium\"\n className={cn('text-center uppercase', labelClassName)}\n >\n {label}\n </Text>\n <Text size=\"md\" className=\"text-center text-text-950\">\n {content?.contentName ?? '—'}\n </Text>\n </div>\n );\n}\n\n/** Best/worst subtema pair; \"—\" when the cut has no answered content. */\nexport function ContentCards({\n best,\n worst,\n}: {\n readonly best: SimulationContentSummary | null;\n readonly worst: SimulationContentSummary | null;\n}) {\n return (\n <div className=\"grid grid-cols-1 gap-2 md:grid-cols-2\">\n <ContentCard\n label=\"Subtema com melhor resultado\"\n labelClassName=\"text-success-300\"\n content={best}\n />\n <ContentCard\n label=\"Subtema com maior dificuldade\"\n labelClassName=\"text-error-300\"\n content={worst}\n />\n </div>\n );\n}\n\n/**\n * Expandable card of one simulado: title and meta line on top, the hit\n * progress below, and whatever the caller renders inside.\n *\n * The two modals list simulados from different endpoints, with different field\n * names, but the card itself is the same object on screen — so the shape lives\n * here and each caller passes the values it has.\n */\nexport function SimulationCardShell({\n value,\n title,\n meta,\n correct,\n totalQuestions,\n expanded,\n onToggle,\n children,\n}: {\n /** Accordion id, unique within the list */\n readonly value: string;\n readonly title: string;\n /** \"Duração · Nota · Feito em\"; an empty string hides the line */\n readonly meta: string;\n readonly correct: number;\n readonly totalQuestions: number;\n readonly expanded: boolean;\n readonly onToggle: () => void;\n readonly children: ReactNode;\n}) {\n return (\n <CardAccordation\n value={value}\n expanded={expanded}\n onToggleExpanded={onToggle}\n triggerClassName=\"p-4\"\n contentClassName=\"flex flex-col gap-4 pt-0\"\n trigger={\n <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n <div className=\"flex items-center justify-between gap-3\">\n <Text size=\"lg\" weight=\"bold\" className=\"min-w-0 text-text-950\">\n {title}\n </Text>\n {meta && (\n <Text\n size=\"xs\"\n weight=\"semibold\"\n className=\"shrink-0 text-text-600\"\n >\n {meta}\n </Text>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n <ProgressBar\n value={correct}\n max={totalQuestions}\n variant=\"green\"\n size=\"small\"\n className=\"flex-1\"\n />\n <Text size=\"xs\" weight=\"medium\" className=\"shrink-0 text-text-950\">\n {`${correct} de ${totalQuestions} corretas`}\n </Text>\n </div>\n </div>\n }\n >\n {children}\n </CardAccordation>\n );\n}\n\n/**\n * Header: avatar, name, an optional badge and \"Escola • Turma • Ano\".\n *\n * `location` takes the parts in display order and drops the empty ones, so a\n * caller whose payload has not arrived yet simply shows the name alone.\n */\nexport function StudentSummaryHeader({\n name,\n location,\n badge,\n}: {\n readonly name: string;\n readonly location: readonly (string | null | undefined)[];\n readonly badge?: ReactNode;\n}) {\n const parts = location.filter((part): part is string => Boolean(part));\n\n return (\n <div className=\"flex flex-col gap-2 border-b border-border-200 pb-4\">\n <div className=\"flex items-center gap-2\">\n <UserIcon size={24} className=\"shrink-0\" />\n <Text size=\"md\" className=\"min-w-0 flex-1 truncate text-text-950\">\n {name}\n </Text>\n {badge}\n </div>\n {parts.length > 0 && (\n <div className=\"flex flex-wrap items-center gap-2\">\n {parts.map((part, index) => (\n <span key={part} className=\"flex items-center gap-2\">\n {index > 0 && (\n <span\n aria-hidden=\"true\"\n className=\"size-1 rounded-full bg-border-600\"\n />\n )}\n <Text size=\"xs\" className=\"text-text-600\">\n {part}\n </Text>\n </span>\n ))}\n </div>\n )}\n </div>\n );\n}\n","/**\n * Format a 0-100 score as a 0-10 grade with one decimal, in pt-BR.\n *\n * The simulados stack carries scores as percentages, while every screen shows\n * them as a grade out of ten. Keeping the conversion in one place is what stops\n * the modal, the table and the cards from rounding differently.\n *\n * @param percentage - Score in the 0-100 scale\n * @returns The grade as a pt-BR string, e.g. `'7,1'`\n *\n * @example\n * ```typescript\n * formatScoreOutOfTen(71.25); // '7,1'\n * formatScoreOutOfTen(100); // '10,0'\n * ```\n */\nexport function formatScoreOutOfTen(percentage: number): string {\n return (percentage / 10).toLocaleString('pt-BR', {\n minimumFractionDigits: 1,\n maximumFractionDigits: 1,\n });\n}\n","/**\n * Format the time a student spent on a single question, the way the simulados\n * card presents it: `40\"`, `2'30\"`, `1h05'30\"`.\n *\n * Returns `null` when there is nothing meaningful to show — the value is zero,\n * absent, negative or not finite. Zero is not the same as \"answered instantly\":\n * simulations answered before per-question telemetry existed carry a zero, and\n * so does a question the student never opened. Rendering `0\"` there would read\n * as \"the student guessed\", which is the opposite of what the teacher should\n * conclude. Callers drop the segment from the label instead.\n *\n * Distinct from `formatTimeSpent` in `activityDetailsUtils`, which renders an\n * activity total as `HH:MM:SS`.\n *\n * @param seconds - Time spent in seconds\n * @returns Formatted duration, or null when there is no measured time\n *\n * @example\n * ```typescript\n * formatQuestionDuration(150); // \"2'30\\\"\"\n * formatQuestionDuration(0); // null\n * ```\n */\nexport function formatQuestionDuration(\n seconds: number | null | undefined\n): string | null {\n if (seconds === null || seconds === undefined || !Number.isFinite(seconds)) {\n return null;\n }\n\n // Truncate before the zero check, not after: a fractional value below one\n // second floors to 0 and must take the same \"not measured\" path as a plain\n // 0, otherwise it would render a misleading `0\"`.\n const total = Math.floor(seconds);\n if (total <= 0) {\n return null;\n }\n\n const hours = Math.floor(total / 3600);\n const minutes = Math.floor((total % 3600) / 60);\n const secs = total % 60;\n\n const paddedSeconds = String(secs).padStart(2, '0');\n\n if (hours > 0) {\n return `${hours}h${String(minutes).padStart(2, '0')}'${paddedSeconds}\"`;\n }\n\n if (minutes > 0) {\n return `${minutes}'${paddedSeconds}\"`;\n }\n\n return `${secs}\"`;\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { BaseApiClient } from '../types/api';\nimport type { NoteData, SimulationDetailData } from '../types/simulations';\nimport { createUseSimulations } from './useSimulations';\n\n/** Lazily loaded question detail of one simulado. */\nexport interface SimulationDetailState {\n readonly loading: boolean;\n readonly error: string | null;\n readonly data: SimulationDetailData | null;\n}\n\n/**\n * Lazily loaded teacher observation of one simulado.\n *\n * `error` exists so a failed request is never shown as \"this simulado has no\n * observation\": that would offer the teacher an empty field whose save is a\n * create-or-replace, silently overwriting a note they were never shown.\n */\nexport interface SimulationNoteState {\n readonly loading: boolean;\n readonly error: string | null;\n readonly data: NoteData | null;\n}\n\n/** Save handler of one simulado's observation, as the note row expects it. */\nexport type SaveSimulationNote = (\n text: string,\n file: File | null,\n existingAttachment: string | null\n) => Promise<void>;\n\n/**\n * What one simulado card needs from this hook: the two loaded pieces and the\n * three actions on them, already bound to that simulado.\n */\nexport interface SimulationCardHandlers {\n readonly detail: SimulationDetailState | undefined;\n readonly note: SimulationNoteState | undefined;\n /** Load the observation again after a failed request */\n readonly onRetryNote: () => void;\n readonly onSaveNote: SaveSimulationNote;\n readonly onSaveQuestionComment: (\n questionId: string,\n comment: string\n ) => Promise<void>;\n}\n\nexport interface UseSimulationCardDetailsParams {\n /** API client used to load and save the per-simulado data */\n readonly api: BaseApiClient;\n /** Enrollment whose simulados are listed; null while there is no student */\n readonly userInstitutionId: string | null;\n /** Whether the modal is open; reopening starts a fresh session */\n readonly isOpen: boolean;\n}\n\nexport interface UseSimulationCardDetailsReturn {\n readonly expandedId: string | null;\n readonly details: Record<string, SimulationDetailState>;\n readonly notes: Record<string, SimulationNoteState>;\n /** Expand or collapse one simulado, loading its data the first time */\n readonly toggle: (simulationId: string) => void;\n /** Load the observation again after a failed request */\n readonly retryNote: (simulationId: string) => void;\n readonly makeSaveNote: (simulationId: string) => SaveSimulationNote;\n readonly makeSaveQuestionComment: (\n simulationId: string\n ) => (questionId: string, comment: string) => Promise<void>;\n}\n\n/**\n * Per-simulado detail state of a student's simulados modal.\n *\n * Owns what both modals that list simulados need: which card is expanded, the\n * questions and observation of each expanded card, and the two save actions.\n * The list itself stays with the caller, because each modal gets it from a\n * different endpoint.\n *\n * Responses are tagged with a session. The session changes whenever the modal\n * opens, the student changes or the API client changes, and a response from an\n * older session is dropped instead of written into the current one.\n *\n * @param params - API client, the enrollment being shown and the open flag\n * @returns Expanded card, loaded details and notes, and the actions on them\n *\n * @example\n * ```tsx\n * const { expandedId, details, notes, toggle } = useSimulationCardDetails({\n * api,\n * userInstitutionId: student?.userInstitutionId ?? null,\n * isOpen,\n * });\n * ```\n */\nexport function useSimulationCardDetails({\n api,\n userInstitutionId,\n isOpen,\n}: UseSimulationCardDetailsParams): UseSimulationCardDetailsReturn {\n const useSimulations = useMemo(() => createUseSimulations(api), [api]);\n const {\n fetchSimulationDetail,\n fetchNote,\n uploadNoteAttachment,\n saveNote,\n saveQuestionComment,\n } = useSimulations();\n\n const [expandedId, setExpandedId] = useState<string | null>(null);\n const [details, setDetails] = useState<Record<string, SimulationDetailState>>(\n {}\n );\n const [notes, setNotes] = useState<Record<string, SimulationNoteState>>({});\n\n // Guards state updates against the modal unmounting mid-request.\n const mountedRef = useRef(true);\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n // Bumped on every new session, so a slow response from the previous student\n // (or from a replaced API client) is dropped instead of written into the\n // current one.\n const sessionRef = useRef(0);\n useEffect(() => {\n sessionRef.current += 1;\n setExpandedId(null);\n setDetails({});\n setNotes({});\n }, [isOpen, userInstitutionId, api]);\n\n const isStale = useCallback(\n (session: number) => !mountedRef.current || sessionRef.current !== session,\n []\n );\n\n const loadDetail = useCallback(\n (enrollmentId: string, simulationId: string) => {\n const session = sessionRef.current;\n setDetails((previous) => ({\n ...previous,\n [simulationId]: { loading: true, error: null, data: null },\n }));\n fetchSimulationDetail(enrollmentId, simulationId)\n .then((data) => {\n if (isStale(session)) return;\n setDetails((previous) => ({\n ...previous,\n [simulationId]: { loading: false, error: null, data },\n }));\n })\n .catch(() => {\n if (isStale(session)) return;\n setDetails((previous) => ({\n ...previous,\n [simulationId]: {\n loading: false,\n error: 'Erro ao carregar o simulado',\n data: null,\n },\n }));\n });\n },\n [fetchSimulationDetail, isStale]\n );\n\n const loadNote = useCallback(\n (enrollmentId: string, simulationId: string) => {\n const session = sessionRef.current;\n setNotes((previous) => ({\n ...previous,\n [simulationId]: { loading: true, error: null, data: null },\n }));\n fetchNote(enrollmentId, simulationId)\n .then((data) => {\n if (isStale(session)) return;\n setNotes((previous) => ({\n ...previous,\n [simulationId]: { loading: false, error: null, data },\n }));\n })\n .catch(() => {\n if (isStale(session)) return;\n setNotes((previous) => ({\n ...previous,\n [simulationId]: {\n loading: false,\n error: 'Erro ao carregar a observação',\n data: null,\n },\n }));\n });\n },\n [fetchNote, isStale]\n );\n\n const toggle = useCallback(\n (simulationId: string) => {\n if (!userInstitutionId) return;\n const next = expandedId === simulationId ? null : simulationId;\n setExpandedId(next);\n if (!next) return;\n\n // The two requests are guarded separately: a loaded detail must not stop\n // the observation from being fetched, and the other way around.\n if (!details[simulationId]) loadDetail(userInstitutionId, simulationId);\n if (!notes[simulationId]) loadNote(userInstitutionId, simulationId);\n },\n [userInstitutionId, expandedId, details, notes, loadDetail, loadNote]\n );\n\n const retryNote = useCallback(\n (simulationId: string) => {\n if (!userInstitutionId) return;\n loadNote(userInstitutionId, simulationId);\n },\n [userInstitutionId, loadNote]\n );\n\n /**\n * Save the observation of one simulado, uploading a newly chosen file first\n * so only its public URL travels with the note.\n */\n const makeSaveNote = useCallback(\n (simulationId: string): SaveSimulationNote =>\n async (text, file, existingAttachment) => {\n if (!userInstitutionId) return;\n const session = sessionRef.current;\n const attachment = file\n ? await uploadNoteAttachment(file)\n : existingAttachment;\n const saved = await saveNote(\n userInstitutionId,\n simulationId,\n text,\n attachment\n );\n if (isStale(session)) return;\n setNotes((previous) => ({\n ...previous,\n [simulationId]: { loading: false, error: null, data: saved },\n }));\n },\n [userInstitutionId, uploadNoteAttachment, saveNote, isStale]\n );\n\n /**\n * Save a comment on one question and reflect it in the loaded detail, so the\n * field shows what the server now holds without a refetch.\n */\n const makeSaveQuestionComment = useCallback(\n (simulationId: string) => async (questionId: string, comment: string) => {\n if (!userInstitutionId) return;\n const session = sessionRef.current;\n const saved = await saveQuestionComment(\n userInstitutionId,\n simulationId,\n questionId,\n comment\n );\n if (isStale(session)) return;\n setDetails((previous) => {\n const current = previous[simulationId];\n if (!current?.data) return previous;\n return {\n ...previous,\n [simulationId]: {\n ...current,\n data: {\n ...current.data,\n questions: current.data.questions.map((question) =>\n question.questionId === questionId\n ? { ...question, teacherComment: saved?.teacherComment ?? '' }\n : question\n ),\n },\n },\n };\n });\n },\n [userInstitutionId, saveQuestionComment, isStale]\n );\n\n return {\n expandedId,\n details,\n notes,\n toggle,\n retryNote,\n makeSaveNote,\n makeSaveQuestionComment,\n };\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/DropdownMenu/DropdownMenu.tsx"],"sourcesContent":["import { CaretRightIcon } from '@phosphor-icons/react/dist/csr/CaretRight';\nimport { SignOutIcon } from '@phosphor-icons/react/dist/csr/SignOut';\nimport { UserIcon } from '@phosphor-icons/react/dist/csr/User';\nimport {\n forwardRef,\n ReactNode,\n ButtonHTMLAttributes,\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n HTMLAttributes,\n MouseEvent,\n KeyboardEvent,\n isValidElement,\n Children,\n cloneElement,\n ReactElement,\n useState,\n Ref,\n RefObject,\n CSSProperties,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { create, StoreApi, useStore } from 'zustand';\nimport Button, { ButtonReadingFluency } from '../Button/Button';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\nimport readingFluencyBird from '../../assets/img/readingFluencyBird.png';\nimport Modal from '../Modal/Modal';\nimport { ThemeToggle } from '../ThemeToggle/ThemeToggle';\nimport type { ThemeMode } from '@/hooks/useTheme';\nimport { useTheme } from '../../hooks/useTheme';\n\ninterface DropdownStore {\n open: boolean;\n setOpen: (open: boolean) => void;\n}\n\ntype DropdownStoreApi = StoreApi<DropdownStore>;\n\nexport function createDropdownStore(): DropdownStoreApi {\n return create<DropdownStore>((set) => ({\n open: false,\n // Bail out when the value didn't change. Skipping `set` here prevents\n // zustand from creating a new state object, which in turn keeps\n // `useStore(store, (s) => s)` subscribers from re-rendering on no-op\n // syncs (e.g. the `useEffect [propOpen]` controlled-mode bridge).\n setOpen: (open) => set((state) => (state.open === open ? state : { open })),\n }));\n}\n\nexport const useDropdownStore = (externalStore?: DropdownStoreApi) => {\n if (!externalStore) {\n throw new Error(\n 'Component must be used within a DropdownMenu (store is missing)'\n );\n }\n\n return externalStore;\n};\n\n/**\n * Keep the same style object when nothing moved — a fresh object on every\n * scroll event would re-render the menu at 60fps for no reason.\n */\nconst PORTAL_STYLE_KEYS = [\n 'top',\n 'bottom',\n 'left',\n 'right',\n 'transform',\n 'maxHeight',\n] as const;\n\nconst isSamePortalStyle = (a: CSSProperties, b: CSSProperties): boolean =>\n PORTAL_STYLE_KEYS.every((key) => a[key] === b[key]);\n\n/** Tallest a portaled menu gets when there is room for it. */\nconst DEFAULT_PORTAL_MAX_HEIGHT = 320;\n\n/** Breathing room between the menu and the edge of the viewport. */\nconst VIEWPORT_MARGIN = 8;\n\n/**\n * Never shrink a menu below this, however cramped the trigger is: a 20px-tall\n * scroller is worse than one that overhangs a little.\n */\nconst MIN_PORTAL_MAX_HEIGHT = 120;\n\ntype PortalSide = 'top' | 'right' | 'bottom' | 'left';\ntype PortalAlign = 'start' | 'center' | 'end';\n\ninterface PortalPlacement {\n side: PortalSide;\n align: PortalAlign;\n sideOffset: number;\n maxHeight: number;\n}\n\n/**\n * Height cap for a portaled menu, given the room it has.\n *\n * The minimum is a floor on the *viewport-derived* room only — it keeps a menu\n * squeezed against an edge from collapsing into a sliver. A caller that asks\n * for a shorter menu than the minimum still gets what it asked for: its value\n * is the ceiling, never something to be raised.\n *\n * @param available - Room in px between the trigger and the viewport edge\n * @param maxHeight - Tallest the caller allows the menu to be\n * @returns The `max-height` to apply, in px\n */\nconst clampMenuHeight = (available: number, maxHeight: number): number =>\n Math.min(maxHeight, Math.max(available, MIN_PORTAL_MAX_HEIGHT));\n\n/**\n * Horizontal edge the menu lines up with, for the `top`/`bottom` sides.\n *\n * @param rect - Trigger bounding rect\n * @param align - Requested alignment\n * @returns The horizontal part of the portal style\n */\nconst alignPortalStyle = (rect: DOMRect, align: PortalAlign): CSSProperties => {\n if (align === 'end') return { right: window.innerWidth - rect.right };\n if (align === 'center') {\n return {\n left: rect.left + rect.width / 2,\n transform: 'translateX(-50%)',\n };\n }\n return { left: rect.left };\n};\n\n/**\n * Place a menu above or below its trigger, sized to the room that exists.\n *\n * A portaled menu is `position: fixed`, so whatever falls past the edge of the\n * viewport can never be scrolled to. The menu therefore takes the space it\n * actually has and scrolls inside it, and turns over to the other side when\n * clamping alone would still leave it overhanging.\n *\n * The flip threshold is the minimum height rather than the maximum: turning\n * over as soon as the menu could not open at full height would send it over the\n * content on any trigger past the middle of the screen, which reads as a\n * glitch. `side=\"top\"` always opens above — the caller asked for it.\n *\n * @param rect - Trigger bounding rect\n * @param placement - Requested side/alignment, offset and height ceiling\n * @returns Style for a fixed menu anchored to the trigger's top or bottom edge\n */\nconst verticalPortalStyle = (\n rect: DOMRect,\n { side, align, sideOffset, maxHeight }: PortalPlacement\n): CSSProperties => {\n const spaceBelow =\n window.innerHeight - rect.bottom - sideOffset - VIEWPORT_MARGIN;\n const spaceAbove = rect.top - sideOffset - VIEWPORT_MARGIN;\n const openAbove =\n side === 'top' ||\n (spaceBelow < Math.min(maxHeight, MIN_PORTAL_MAX_HEIGHT) &&\n spaceAbove > spaceBelow);\n\n return {\n maxHeight: clampMenuHeight(openAbove ? spaceAbove : spaceBelow, maxHeight),\n overflowY: 'auto',\n ...(openAbove\n ? { bottom: window.innerHeight - rect.top + sideOffset }\n : { top: rect.bottom + sideOffset }),\n ...alignPortalStyle(rect, align),\n };\n};\n\n/**\n * Place a menu beside its trigger, sized to the room below the trigger's top.\n *\n * Anchored by the edge that faces the trigger: a `left` for `side=\"left\"` would\n * grow the menu back over the trigger it was meant to sit outside of. It runs\n * downwards from the trigger's top, so it needs the same clamping as a menu\n * opening below — without it a long list disappears past the viewport bottom.\n *\n * @param rect - Trigger bounding rect\n * @param placement - Requested side, offset and height ceiling\n * @returns Style for a fixed menu anchored to the trigger's left or right edge\n */\nconst horizontalPortalStyle = (\n rect: DOMRect,\n { side, sideOffset, maxHeight }: PortalPlacement\n): CSSProperties => ({\n top: rect.top,\n maxHeight: clampMenuHeight(\n window.innerHeight - rect.top - VIEWPORT_MARGIN,\n maxHeight\n ),\n overflowY: 'auto',\n ...(side === 'left'\n ? { right: window.innerWidth - rect.left + sideOffset }\n : { left: rect.right + sideOffset }),\n});\n\n/**\n * Merge two refs into one. Returns the single ref when only one is set (no\n * callback churn), or `undefined` when neither is — so a cloned child without a\n * ref stays refless.\n */\nconst mergeRefs = <T,>(\n a: Ref<T> | undefined,\n b: Ref<T> | undefined\n): Ref<T> | undefined => {\n if (!a) return b;\n if (!b) return a;\n return (node: T | null) => {\n for (const r of [a, b]) {\n if (typeof r === 'function') r(node);\n else (r as RefObject<T | null>).current = node;\n }\n };\n};\n\nconst injectStore = (\n children: ReactNode,\n store: DropdownStoreApi\n): ReactNode => {\n return Children.map(children, (child) => {\n if (isValidElement(child)) {\n const typedChild = child as ReactElement<{\n store?: DropdownStoreApi;\n children?: ReactNode;\n }>;\n\n // Aqui tu checa o displayName do componente\n const displayName = (\n typedChild.type as unknown as { displayName: string }\n ).displayName;\n\n // Lista de componentes que devem receber o store\n const allowed = [\n 'DropdownMenuTrigger',\n 'DropdownContent',\n 'DropdownMenuContent',\n 'DropdownMenuSeparator',\n 'DropdownMenuItem',\n 'MenuLabel',\n 'ProfileMenuTrigger',\n 'ProfileMenuHeader',\n 'ProfileMenuFooter',\n 'ProfileToggleTheme',\n 'ProfileMenuReadingFluencyTrigger',\n 'ProfileMenuReadingFluencyFooter',\n ];\n\n let newProps: Partial<{\n store: DropdownStoreApi;\n children: ReactNode;\n }> = {};\n\n if (allowed.includes(displayName)) {\n newProps.store = store;\n }\n\n if (typedChild.props.children) {\n newProps.children = injectStore(typedChild.props.children, store);\n }\n\n return cloneElement(typedChild, newProps);\n }\n\n return child;\n });\n};\n\ninterface DropdownMenuProps {\n children: ReactNode;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\nconst DropdownMenu = ({\n children,\n open: propOpen,\n onOpenChange,\n}: DropdownMenuProps) => {\n const storeRef = useRef<DropdownStoreApi | null>(null);\n storeRef.current ??= createDropdownStore();\n const store = storeRef.current;\n const { open, setOpen: storeSetOpen } = useStore(store, (s) => s);\n\n const setOpen = (newOpen: boolean) => {\n storeSetOpen(newOpen);\n };\n\n const menuRef = useRef<HTMLDivElement | null>(null);\n\n const handleArrowDownOrArrowUp = (event: globalThis.KeyboardEvent) => {\n // A portaled content lives in document.body, outside menuRef — look it up\n // globally in that case. Only the open menu carries data-open=\"true\"\n // (a closing one keeps rendering for the 200ms fade-out).\n const menuContent =\n menuRef.current?.querySelector('[role=\"menu\"]') ??\n document.querySelector(\n '[data-dropdown-content=\"true\"][data-open=\"true\"]'\n );\n if (menuContent) {\n event.preventDefault();\n\n const items = Array.from(\n menuContent.querySelectorAll(\n '[role=\"menuitem\"]:not([aria-disabled=\"true\"])'\n )\n ).filter((el): el is HTMLElement => el instanceof HTMLElement);\n\n if (items.length === 0) return;\n\n const focusedItem = document.activeElement as HTMLElement;\n const currentIndex = items.indexOf(focusedItem);\n\n let nextIndex;\n if (event.key === 'ArrowDown') {\n nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % items.length;\n } else {\n // ArrowUp\n nextIndex =\n currentIndex === -1\n ? items.length - 1\n : (currentIndex - 1 + items.length) % items.length;\n }\n\n items[nextIndex]?.focus();\n }\n };\n\n const handleDownkey = (event: globalThis.KeyboardEvent) => {\n if (event.key === 'Escape') {\n setOpen(false);\n } else if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n handleArrowDownOrArrowUp(event);\n }\n };\n\n const handleClickOutside = (event: Event) => {\n const target = event.target as Node;\n\n if (menuRef.current?.contains(target)) {\n return;\n }\n\n if (\n target instanceof Element &&\n target.closest('[data-dropdown-content=\"true\"]')\n ) {\n return;\n }\n\n setOpen(false);\n };\n\n useEffect(() => {\n if (open) {\n document.addEventListener('pointerdown', handleClickOutside);\n document.addEventListener('keydown', handleDownkey);\n }\n\n return () => {\n document.removeEventListener('pointerdown', handleClickOutside);\n document.removeEventListener('keydown', handleDownkey);\n };\n }, [open]);\n\n useEffect(() => {\n onOpenChange?.(open);\n }, [open, onOpenChange]);\n\n useEffect(() => {\n if (propOpen !== undefined) {\n setOpen(propOpen);\n }\n }, [propOpen]);\n\n return (\n <div className=\"relative\" ref={menuRef}>\n {injectStore(children, store)}\n </div>\n );\n};\n\n// Componentes genéricos do DropdownMenu\nconst DropdownMenuTrigger = forwardRef<\n HTMLButtonElement,\n ButtonHTMLAttributes<HTMLButtonElement> & {\n disabled?: boolean;\n store?: DropdownStoreApi;\n /**\n * Render the single child as the trigger instead of wrapping it in a\n * `<button>`. Use when the child is itself a button/interactive element, so\n * the trigger doesn't nest one button inside another (invalid HTML). The\n * child keeps its own props; the toggle behaviour and `aria-expanded` are\n * merged in.\n */\n asChild?: boolean;\n }\n>(\n (\n { className, children, onClick, store: externalStore, asChild, ...props },\n ref\n ) => {\n const store = useDropdownStore(externalStore);\n\n const open = useStore(store, (s) => s.open);\n const toggleOpen = () => store.setState({ open: !open });\n\n const handleTriggerClick = (e: MouseEvent<HTMLElement>) => {\n e.stopPropagation();\n toggleOpen();\n onClick?.(e as MouseEvent<HTMLButtonElement>);\n };\n\n // `injectStore` re-wraps children through Children.map, so `children` may be\n // a single-element array rather than a bare element — unwrap it here.\n const asChildElement = asChild\n ? (Children.toArray(children).find(isValidElement) as\n | ReactElement<{\n className?: string;\n onClick?: (e: MouseEvent<HTMLElement>) => void;\n ref?: Ref<HTMLElement>;\n }>\n | undefined)\n : undefined;\n\n if (asChildElement) {\n const child = asChildElement;\n return cloneElement(child, {\n ...props,\n ref: mergeRefs(ref, child.props.ref),\n className: cn(child.props.className, className),\n 'aria-expanded': open,\n onClick: (e: MouseEvent<HTMLElement>) => {\n // Keep the child's own handler, then run the trigger's toggle — same\n // order as the previous nested-button version.\n child.props.onClick?.(e);\n handleTriggerClick(e);\n },\n } as Partial<HTMLAttributes<HTMLElement>> & { store?: DropdownStoreApi });\n }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n onClick={handleTriggerClick}\n aria-expanded={open}\n className={cn(\n 'appearance-none bg-transparent border-none p-0',\n className\n )}\n {...props}\n >\n {children}\n </button>\n );\n }\n);\nDropdownMenuTrigger.displayName = 'DropdownMenuTrigger';\n\nconst ITEM_SIZE_CLASSES = {\n small: 'text-sm',\n medium: 'text-md',\n} as const;\n\nconst SIDE_CLASSES = {\n top: 'bottom-full',\n right: 'top-full',\n bottom: 'top-full',\n left: 'top-full',\n};\n\nconst ALIGN_CLASSES = {\n start: 'left-0',\n center: 'left-1/2 -translate-x-1/2',\n end: 'right-0',\n};\n\n// O \"chrome\" do card (bg/borda/rounded/shadow) fica por variante para que a\n// variante `papole` possa ter o seu próprio (fundo secondary-500, sem borda,\n// sombra das vars --sds-* do Figma traduzidas: 0 8px 8px -2px, preto 10% + 20%).\n// `menu`/`profile` mantêm exatamente o visual de antes.\nconst MENUCONTENT_VARIANT_CLASSES = {\n menu: 'bg-background rounded-md border bg-popover text-popover-foreground shadow-md border-border-100 p-1',\n profile:\n 'bg-background rounded-md border bg-popover text-popover-foreground shadow-md border-border-100 p-6',\n papole:\n 'flex flex-col gap-4 bg-secondary-500 rounded-[20px] pt-6 px-6 pb-8 [box-shadow:0px_8px_8px_-2px_#0000001a,0px_8px_8px_-2px_#00000033]',\n};\n\nconst MenuLabel = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement> & {\n inset?: boolean;\n store?: DropdownStoreApi;\n }\n>(({ className, inset, store: _store, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={cn('text-sm w-full', inset ? 'pl-8' : '', className)}\n {...props}\n />\n );\n});\nMenuLabel.displayName = 'MenuLabel';\n\nconst DropdownMenuContent = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement> & {\n align?: 'start' | 'center' | 'end';\n side?: 'top' | 'right' | 'bottom' | 'left';\n variant?: 'menu' | 'profile' | 'papole';\n sideOffset?: number;\n store?: DropdownStoreApi;\n portal?: boolean;\n triggerRef?: RefObject<HTMLElement | null>;\n /**\n * Tallest the menu gets, in px, when the viewport allows it. Portaled menus\n * only — it is capped by the space actually available around the trigger.\n */\n maxHeight?: number;\n }\n>(\n (\n {\n className,\n align = 'start',\n side = 'bottom',\n variant = 'menu',\n sideOffset = 4,\n children,\n store: externalStore,\n portal = false,\n triggerRef,\n maxHeight = DEFAULT_PORTAL_MAX_HEIGHT,\n ...props\n },\n ref\n ) => {\n const store = useDropdownStore(externalStore);\n const open = useStore(store, (s) => s.open);\n const [isVisible, setIsVisible] = useState(open);\n const [portalStyle, setPortalStyle] = useState<CSSProperties>({});\n const contentRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (open) {\n setIsVisible(true);\n } else {\n const timer = setTimeout(() => setIsVisible(false), 200);\n return () => clearTimeout(timer);\n }\n }, [open]);\n\n const updatePortalPosition = useCallback(() => {\n if (!portal || !triggerRef?.current) return;\n\n const rect = triggerRef.current.getBoundingClientRect();\n const placement = { side, align, sideOffset, maxHeight };\n const style =\n side === 'left' || side === 'right'\n ? horizontalPortalStyle(rect, placement)\n : verticalPortalStyle(rect, placement);\n\n setPortalStyle((prev) => (isSamePortalStyle(prev, style) ? prev : style));\n }, [portal, triggerRef, align, side, sideOffset, maxHeight]);\n\n useLayoutEffect(() => {\n if (open) updatePortalPosition();\n }, [open, updatePortalPosition]);\n\n // A portaled menu is position:fixed, so it doesn't follow the trigger when\n // an ancestor scrolls. Tables wrap their body in `overflow-x-auto`, so a\n // menu anchored to a column header would drift away from its <th> on any\n // horizontal scroll. Capture-phase catches scrolls on any ancestor, not\n // just the window.\n useEffect(() => {\n if (!portal || !open) return;\n\n const reposition = () => updatePortalPosition();\n window.addEventListener('scroll', reposition, true);\n window.addEventListener('resize', reposition);\n\n return () => {\n window.removeEventListener('scroll', reposition, true);\n window.removeEventListener('resize', reposition);\n };\n }, [portal, open, updatePortalPosition]);\n\n if (!isVisible) return null;\n\n const getPositionClasses = () => {\n if (portal) {\n return 'fixed';\n }\n const vertical = SIDE_CLASSES[side];\n const horizontal = ALIGN_CLASSES[align];\n\n return `absolute ${vertical} ${horizontal}`;\n };\n\n const variantClasses = MENUCONTENT_VARIANT_CLASSES[variant];\n\n const content = (\n <div\n ref={portal ? contentRef : ref}\n role=\"menu\"\n data-dropdown-content=\"true\"\n data-open={open}\n className={`\n z-50 min-w-[210px] overflow-hidden\n ${open ? 'animate-in fade-in-0 zoom-in-95' : 'animate-out fade-out-0 zoom-out-95'}\n ${getPositionClasses()}\n ${variantClasses}\n ${className}\n `}\n style={{\n ...(portal\n ? portalStyle\n : {\n marginTop: side === 'bottom' ? sideOffset : undefined,\n marginBottom: side === 'top' ? sideOffset : undefined,\n marginLeft: side === 'right' ? sideOffset : undefined,\n marginRight: side === 'left' ? sideOffset : undefined,\n }),\n }}\n {...props}\n >\n {children}\n </div>\n );\n\n if (portal && typeof document !== 'undefined') {\n return createPortal(content, document.body);\n }\n\n return content;\n }\n);\nDropdownMenuContent.displayName = 'DropdownMenuContent';\n\nconst DropdownMenuItem = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement> & {\n inset?: boolean;\n size?: 'small' | 'medium';\n iconLeft?: ReactNode;\n iconRight?: ReactNode;\n disabled?: boolean;\n variant?: 'profile' | 'menu';\n store?: DropdownStoreApi;\n preventClose?: boolean;\n }\n>(\n (\n {\n className,\n size = 'small',\n children,\n iconRight,\n iconLeft,\n disabled = false,\n onClick,\n variant = 'menu',\n store: externalStore,\n preventClose = false,\n ...props\n },\n ref\n ) => {\n const store = useDropdownStore(externalStore);\n const setOpen = useStore(store, (s) => s.setOpen);\n const sizeClasses = ITEM_SIZE_CLASSES[size];\n\n const handleClick = (\n e: MouseEvent<HTMLDivElement> | KeyboardEvent<HTMLDivElement>\n ) => {\n if (disabled) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n if (e.type === 'click') {\n onClick?.(e as MouseEvent<HTMLDivElement>);\n } else if (e.type === 'keydown') {\n // For keyboard events, trigger click if Enter or Space was pressed\n const keyEvent = e as KeyboardEvent<HTMLDivElement>;\n if (keyEvent.key === 'Enter' || keyEvent.key === ' ') {\n (e.currentTarget as HTMLElement).click();\n }\n // honor any user-provided key handler\n props.onKeyDown?.(keyEvent);\n }\n if (!preventClose) {\n setOpen(false);\n }\n };\n\n const getVariantClasses = () => {\n if (variant === 'profile') {\n return 'relative flex flex-row justify-between select-none items-center gap-2 rounded-sm p-4 text-sm outline-none transition-colors [&>svg]:size-6 [&>svg]:shrink-0';\n }\n return 'relative flex select-none items-center gap-2 rounded-sm p-3 text-sm outline-none transition-colors [&>svg]:size-4 [&>svg]:shrink-0';\n };\n\n const getVariantProps = () => {\n return variant === 'profile' ? { 'data-variant': 'profile' } : {};\n };\n\n return (\n <div\n ref={ref}\n role=\"menuitem\"\n {...getVariantProps()}\n aria-disabled={disabled}\n className={`\n focus-visible:bg-background-50\n ${getVariantClasses()}\n ${sizeClasses}\n ${className}\n ${\n disabled\n ? 'cursor-not-allowed text-text-400'\n : 'cursor-pointer hover:bg-background-50 text-text-700 focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground'\n }\n `}\n onClick={handleClick}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n e.stopPropagation();\n handleClick(e);\n }\n }}\n tabIndex={disabled ? -1 : 0}\n {...props}\n >\n {iconLeft}\n <div className=\"w-full\">{children}</div>\n {iconRight}\n </div>\n );\n }\n);\nDropdownMenuItem.displayName = 'DropdownMenuItem';\n\nconst DropdownMenuSeparator = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement> & { store?: DropdownStoreApi }\n>(({ className, store: _store, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('my-1 h-px bg-border-200', className)}\n {...props}\n />\n));\nDropdownMenuSeparator.displayName = 'DropdownMenuSeparator';\n\n// Componentes específicos do ProfileMenu\nconst ProfileMenuTrigger = forwardRef<\n HTMLButtonElement,\n ButtonHTMLAttributes<HTMLButtonElement> & { store?: DropdownStoreApi }\n>(({ className, onClick, store: externalStore, ...props }, ref) => {\n const store = useDropdownStore(externalStore);\n const open = useStore(store, (s) => s.open);\n const toggleOpen = () => store.setState({ open: !open });\n\n return (\n <button\n ref={ref}\n className={cn(\n 'rounded-lg size-10 bg-primary-50 flex items-center justify-center cursor-pointer',\n className\n )}\n onClick={(e) => {\n e.stopPropagation();\n toggleOpen();\n onClick?.(e);\n }}\n aria-expanded={open}\n {...props}\n >\n <span className=\"size-6 rounded-full bg-primary-100 flex items-center justify-center\">\n <UserIcon className=\"text-primary-950\" size={18} />\n </span>\n </button>\n );\n});\nProfileMenuTrigger.displayName = 'ProfileMenuTrigger';\n\nconst ProfileMenuHeader = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement> & {\n name: string;\n email: string;\n photoUrl?: string | null;\n store?: DropdownStoreApi;\n }\n>(({ className, name, email, photoUrl, store: _store, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-component=\"ProfileMenuHeader\"\n className={cn(\n 'flex flex-row gap-4 items-center min-w-[280px]',\n className\n )}\n {...props}\n >\n <span className=\"w-16 h-16 bg-primary-100 rounded-full flex items-center justify-center overflow-hidden flex-shrink-0\">\n {photoUrl ? (\n <img\n src={photoUrl}\n alt=\"Foto de perfil\"\n className=\"w-full h-full object-cover\"\n />\n ) : (\n <UserIcon size={34} className=\"text-primary-800\" />\n )}\n </span>\n <div className=\"flex flex-col min-w-0\">\n <Text\n size=\"xl\"\n weight=\"bold\"\n color=\"text-text-950\"\n className=\"truncate\"\n >\n {name}\n </Text>\n <Text size=\"md\" color=\"text-text-600\" className=\"truncate\">\n {email}\n </Text>\n </div>\n </div>\n );\n});\nProfileMenuHeader.displayName = 'ProfileMenuHeader';\n\nconst ProfileMenuInfo = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement> & {\n schoolName: string;\n classYearName: string;\n schoolYearName: string;\n store?: DropdownStoreApi;\n }\n>(\n (\n {\n className,\n schoolName,\n classYearName,\n schoolYearName,\n store: _store,\n ...props\n },\n ref\n ) => {\n if (!schoolName && !classYearName && !schoolYearName) {\n return null;\n }\n\n return (\n <div\n ref={ref}\n data-component=\"ProfileMenuInfo\"\n className={cn('flex flex-row gap-4 items-center', className)}\n {...props}\n >\n <span className=\"w-16 h-16\" />\n <div className=\"flex flex-col \">\n {schoolName && (\n <Text size=\"md\" color=\"text-text-600\">\n {schoolName}\n </Text>\n )}\n\n {(classYearName || schoolYearName) && (\n <span className=\"flex flex-row items-center gap-2\">\n {classYearName && (\n <Text size=\"md\" color=\"text-text-600\">\n {classYearName}\n </Text>\n )}\n {classYearName && schoolYearName && (\n <Text size=\"md\" color=\"text-text-600\">\n ●\n </Text>\n )}\n {schoolYearName && (\n <Text size=\"md\" color=\"text-text-600\">\n {schoolYearName}\n </Text>\n )}\n </span>\n )}\n </div>\n </div>\n );\n }\n);\nProfileMenuInfo.displayName = 'ProfileMenuInfo';\n\nconst ProfileToggleTheme = ({\n store: externalStore,\n ...props\n}: HTMLAttributes<HTMLDivElement> & { store?: DropdownStoreApi }) => {\n const { themeMode, setTheme, isThemeLocked } = useTheme();\n const [modalThemeToggle, setModalThemeToggle] = useState(false);\n const [selectedTheme, setSelectedTheme] = useState<ThemeMode>(themeMode);\n\n const internalStoreRef = useRef<DropdownStoreApi | null>(null);\n internalStoreRef.current ??= createDropdownStore();\n const store = externalStore ?? internalStoreRef.current;\n const setOpen = useStore(store, (s) => s.setOpen);\n\n const handleClick = (e: MouseEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n setModalThemeToggle(true);\n };\n\n const handleSave = () => {\n setTheme(selectedTheme);\n setModalThemeToggle(false);\n setOpen(false); // Close dropdown after saving\n };\n\n const handleCancel = () => {\n setSelectedTheme(themeMode); // Reset to current theme\n setModalThemeToggle(false);\n setOpen(false); // Close dropdown after canceling\n };\n\n // Tema travado pelo produto (ex.: Fluência Leitora): esconde a entrada do\n // menu inteira. Deixá-la visível daria um modal que salva e não muda nada.\n if (isThemeLocked) return null;\n\n return (\n <>\n <DropdownMenuItem\n variant=\"profile\"\n preventClose={true}\n store={store}\n iconLeft={\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 25 25\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M12.5 2.75C15.085 2.75276 17.5637 3.78054 19.3916 5.6084C21.2195 7.43628 22.2473 9.915 22.25 12.5C22.25 14.4284 21.6778 16.3136 20.6064 17.917C19.5352 19.5201 18.0128 20.7699 16.2314 21.5078C14.4499 22.2458 12.489 22.4387 10.5977 22.0625C8.70642 21.6863 6.96899 20.758 5.60547 19.3945C4.24197 18.031 3.31374 16.2936 2.9375 14.4023C2.56129 12.511 2.75423 10.5501 3.49219 8.76855C4.23012 6.98718 5.47982 5.46483 7.08301 4.39355C8.68639 3.32221 10.5716 2.75 12.5 2.75ZM11.75 4.28516C9.70145 4.47452 7.7973 5.42115 6.41016 6.94043C5.02299 8.4599 4.25247 10.4426 4.25 12.5C4.25247 14.5574 5.02299 16.5401 6.41016 18.0596C7.7973 19.5789 9.70145 20.5255 11.75 20.7148V4.28516Z\"\n fill=\"currentColor\"\n />\n </svg>\n }\n iconRight={<CaretRightIcon />}\n onClick={handleClick}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n e.stopPropagation();\n setModalThemeToggle(true);\n }\n }}\n {...props}\n >\n <Text size=\"md\" color=\"text-text-700\">\n Aparência\n </Text>\n </DropdownMenuItem>\n\n <Modal\n isOpen={modalThemeToggle}\n onClose={handleCancel}\n title=\"Aparência\"\n size=\"md\"\n footer={\n <div className=\"flex gap-3\">\n <Button variant=\"outline\" onClick={handleCancel}>\n Cancelar\n </Button>\n <Button variant=\"solid\" onClick={handleSave}>\n Salvar\n </Button>\n </div>\n }\n >\n <div className=\"flex flex-col\">\n <p className=\"text-sm text-text-500\">Escolha o tema:</p>\n <ThemeToggle variant=\"with-save\" onToggle={setSelectedTheme} />\n </div>\n </Modal>\n </>\n );\n};\nProfileToggleTheme.displayName = 'ProfileToggleTheme';\n\nconst ProfileMenuSection = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement> & { store?: DropdownStoreApi }\n>(({ className, children, store: _store, ...props }, ref) => {\n return (\n <div ref={ref} className={cn('flex flex-col p-2', className)} {...props}>\n {children}\n </div>\n );\n});\nProfileMenuSection.displayName = 'ProfileMenuSection';\n\nconst ProfileMenuFooter = ({\n className,\n disabled = false,\n onClick,\n store: externalStore,\n ...props\n}: HTMLAttributes<HTMLButtonElement> & {\n disabled?: boolean;\n store?: DropdownStoreApi;\n}) => {\n const store = useDropdownStore(externalStore);\n const setOpen = useStore(store, (s) => s.setOpen);\n\n return (\n <Button\n variant=\"outline\"\n className={cn('w-full', className)}\n disabled={disabled}\n onClick={(e) => {\n setOpen(false);\n onClick?.(e);\n }}\n {...props}\n >\n <span className=\"mr-2 flex items-center\">\n <SignOutIcon className=\"text-inherit\" />\n </span>\n <Text color=\"inherit\">Sair</Text>\n </Button>\n );\n};\nProfileMenuFooter.displayName = 'ProfileMenuFooter';\n\n// ======================================================================\n// Componentes do ProfileMenu Reading Fluency (usados com variant=\"papole\")\n// ======================================================================\n\n// Trigger de perfil Reading Fluency: ícone circular de 42px. Mostra a foto do usuário\n// (`photoUrl`) e cai no passarinho quando não houver.\nconst ProfileMenuReadingFluencyTrigger = forwardRef<\n HTMLButtonElement,\n ButtonHTMLAttributes<HTMLButtonElement> & {\n photoUrl?: string | null;\n store?: DropdownStoreApi;\n }\n>(({ className, onClick, photoUrl, store: externalStore, ...props }, ref) => {\n const store = useDropdownStore(externalStore);\n const open = useStore(store, (s) => s.open);\n const toggleOpen = () => store.setState({ open: !open });\n\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn(\n 'size-[42px] rounded-full overflow-hidden cursor-pointer bg-secondary-600 flex items-center justify-center',\n className\n )}\n onClick={(e) => {\n e.stopPropagation();\n toggleOpen();\n onClick?.(e);\n }}\n aria-expanded={open}\n aria-label=\"Abrir menu de perfil\"\n {...props}\n >\n <img\n src={photoUrl ?? readingFluencyBird}\n alt=\"Foto de perfil\"\n className=\"w-full h-full object-cover\"\n />\n </button>\n );\n});\nProfileMenuReadingFluencyTrigger.displayName =\n 'ProfileMenuReadingFluencyTrigger';\n\n// Card de informações do aluno (fundo secondary-600). Dois blocos texto1/texto2:\n// nome/email (sempre) e escola/turma (quando houver).\nconst ProfileMenuReadingFluencyInfo = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement> & {\n name: string;\n email: string;\n schoolName?: string;\n classYearName?: string;\n schoolYearName?: string;\n store?: DropdownStoreApi;\n }\n>(\n (\n {\n className,\n name,\n email,\n schoolName,\n classYearName,\n schoolYearName,\n store: _store,\n ...props\n },\n ref\n ) => {\n const hasSchoolBlock = !!(schoolName || classYearName || schoolYearName);\n\n return (\n <div\n ref={ref}\n data-component=\"ProfileMenuReadingFluencyInfo\"\n className={cn(\n 'font-quicksand flex flex-col gap-4 rounded-[20px] bg-secondary-600 p-4',\n className\n )}\n {...props}\n >\n <div className=\"flex flex-col\">\n <Text size=\"md\" className=\"font-bold text-text-100 truncate\">\n {name}\n </Text>\n <Text size=\"sm\" className=\"font-medium text-secondary-300 truncate\">\n {email}\n </Text>\n </div>\n\n {hasSchoolBlock && (\n <div className=\"flex flex-col\">\n {schoolName && (\n <Text size=\"md\" className=\"font-bold text-text-100 truncate\">\n {schoolName}\n </Text>\n )}\n {(classYearName || schoolYearName) && (\n <Text\n size=\"sm\"\n className=\"font-medium text-secondary-300 truncate\"\n >\n {[classYearName, schoolYearName].filter(Boolean).join(' • ')}\n </Text>\n )}\n </div>\n )}\n </div>\n );\n }\n);\nProfileMenuReadingFluencyInfo.displayName = 'ProfileMenuReadingFluencyInfo';\n\n// Rodapé Reading Fluency: botão \"Sair\" (ButtonReadingFluency outline md). Fecha o menu e repassa\n// o onClick (logout).\nconst ProfileMenuReadingFluencyFooter = ({\n className,\n disabled = false,\n onClick,\n store: externalStore,\n ...props\n}: ButtonHTMLAttributes<HTMLButtonElement> & {\n disabled?: boolean;\n store?: DropdownStoreApi;\n}) => {\n const store = useDropdownStore(externalStore);\n const setOpen = useStore(store, (s) => s.setOpen);\n\n return (\n <ButtonReadingFluency\n variant=\"outline-inverse\"\n size=\"medium\"\n className={cn('w-full', className)}\n disabled={disabled}\n onClick={(e) => {\n setOpen(false);\n onClick?.(e);\n }}\n {...props}\n >\n Sair\n </ButtonReadingFluency>\n );\n};\nProfileMenuReadingFluencyFooter.displayName = 'ProfileMenuReadingFluencyFooter';\n\n// Exportações\nexport default DropdownMenu;\nexport {\n // Componentes genéricos\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n MenuLabel,\n DropdownMenuSeparator,\n\n // Componentes específicos do ProfileMenu\n ProfileMenuTrigger,\n ProfileMenuHeader,\n ProfileMenuSection,\n ProfileMenuFooter,\n ProfileToggleTheme,\n ProfileMenuInfo,\n\n // Componentes do ProfileMenu Reading Fluency\n ProfileMenuReadingFluencyTrigger,\n ProfileMenuReadingFluencyInfo,\n ProfileMenuReadingFluencyFooter,\n};\n"],"mappings":"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","names":[]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|