UNPKG

analytica-frontend-lib

Version:

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

1 lines 501 kB
{"version":3,"sources":["../../src/components/ActivitiesHistory/ActivitiesHistory.tsx","../../src/utils/utils.ts","../../src/components/Text/Text.tsx","../../src/components/Menu/Menu.tsx","../../src/components/ActivitiesHistory/tabs/HistoryTab.tsx","../../src/components/Button/Button.tsx","../../src/components/EmptyState/EmptyState.tsx","../../src/components/TableProvider/TableProvider.tsx","../../src/components/Table/Table.tsx","../../src/components/NoSearchResult/NoSearchResult.tsx","../../src/components/Skeleton/Skeleton.tsx","../../src/components/Table/TablePagination.tsx","../../src/components/Filter/useTableFilter.ts","../../src/components/Search/Search.tsx","../../src/components/DropdownMenu/DropdownMenu.tsx","../../src/components/Modal/Modal.tsx","../../src/components/Modal/utils/videoUtils.ts","../../src/components/ThemeToggle/ThemeToggle.tsx","../../src/components/SelectionButton/SelectionButton.tsx","../../src/hooks/useTheme.ts","../../src/store/themeStore.ts","../../src/components/CheckBoxGroup/CheckBoxGroup.tsx","../../src/components/Badge/Badge.tsx","../../src/components/IconButton/IconButton.tsx","../../src/components/CheckBox/CheckBox.tsx","../../src/components/Divider/Divider.tsx","../../src/components/Toast/Toast.tsx","../../src/components/Toast/utils/ToastStore.ts","../../src/components/Toast/utils/Toaster.tsx","../../src/components/ProgressBar/ProgressBar.tsx","../../src/components/Accordation/Accordation.tsx","../../src/components/Card/Card.tsx","../../src/components/IconRender/IconRender.tsx","../../src/assets/icons/subjects/ChatPT.tsx","../../src/assets/icons/subjects/ChatEN.tsx","../../src/assets/icons/subjects/ChatES.tsx","../../src/components/Accordation/AccordionGroup.tsx","../../src/components/AlertDialog/AlertDialog.tsx","../../src/assets/icons/subjects/BookOpenText.tsx","../../src/assets/icons/subjects/HeadCircuit.tsx","../../src/assets/icons/subjects/Microscope.tsx","../../src/components/SubjectInfo/SubjectInfo.tsx","../../src/types/common.ts","../../src/hooks/useActivitiesHistory.ts","../../src/types/activitiesHistory.ts","../../src/utils/hookErrorHandler.ts","../../src/hooks/useActivityModels.ts","../../src/components/CheckBoxGroup/CheckBoxGroup.helpers.ts","../../src/components/Filter/FilterModal.tsx","../../src/components/ActivitiesHistory/components/ErrorDisplay.tsx","../../src/components/ActivitiesHistory/config/historyTableColumns.tsx","../../src/components/ActivitiesHistory/utils/renderSubjectCell.tsx","../../src/components/ActivitiesHistory/utils/renderTruncatedText.tsx","../../src/components/ActivitiesHistory/utils/filterBuilders.ts","../../src/components/ActivitiesHistory/utils/filterOptions.ts","../../src/components/ActivitiesHistory/config/historyFiltersConfig.ts","../../src/components/ActivitiesHistory/tabs/ModelsTab.tsx","../../src/components/ActivitiesHistory/config/modelsTableColumns.tsx","../../src/components/ActivitiesHistory/config/modelsFiltersConfig.ts","../../src/components/ActivitiesHistory/tabs/DraftsTab.tsx"],"sourcesContent":["import { useState } from 'react';\nimport Text from '../Text/Text';\nimport { Menu, MenuItem, MenuContent } from '../Menu/Menu';\nimport { HistoryTab } from './tabs/HistoryTab';\nimport { ModelsTab } from './tabs/ModelsTab';\nimport { DraftsTab } from './tabs/DraftsTab';\nimport type { SubjectEnum } from '../../enums/SubjectEnum';\nimport type {\n ActivityTableItem,\n ActivityHistoryFilters,\n ActivitiesHistoryApiResponse,\n ActivityUserFilterData,\n ActivityModelTableItem,\n ActivityModelFilters,\n ActivityModelsApiResponse,\n} from '../../types/activitiesHistory';\n\n/**\n * Enum for page tabs\n */\nenum PageTab {\n HISTORY = 'history',\n DRAFTS = 'drafts',\n MODELS = 'models',\n}\n\n/**\n * Page titles based on active tab\n */\nconst PAGE_TITLES: Record<PageTab, string> = {\n [PageTab.HISTORY]: 'Histórico de atividades',\n [PageTab.DRAFTS]: 'Rascunhos',\n [PageTab.MODELS]: 'Modelos de atividades',\n};\n\n/**\n * Props for the ActivitiesHistory component\n */\nexport interface ActivitiesHistoryProps {\n /** Function to fetch activities history from API */\n fetchActivitiesHistory: (\n filters?: ActivityHistoryFilters\n ) => Promise<ActivitiesHistoryApiResponse>;\n /** Function to fetch activity models from API */\n fetchActivityModels: (\n filters?: ActivityModelFilters\n ) => Promise<ActivityModelsApiResponse>;\n /** Function to delete an activity model */\n deleteActivityModel: (id: string) => Promise<void>;\n /** Callback when create activity button is clicked */\n onCreateActivity: () => void;\n /** Callback when create model button is clicked */\n onCreateModel: () => void;\n /** Callback when a history row is clicked */\n onRowClick: (row: ActivityTableItem) => void;\n /** Callback when send activity button is clicked on a model */\n onSendActivity?: (model: ActivityModelTableItem) => void;\n /** Callback when edit model button is clicked */\n onEditModel?: (model: ActivityModelTableItem) => void;\n /** Image for empty state */\n emptyStateImage?: string;\n /** Image for no search results */\n noSearchImage?: string;\n /** Function to map subject name to SubjectEnum */\n mapSubjectNameToEnum?: (subjectName: string) => SubjectEnum | null;\n /** User data for populating filter options */\n userFilterData?: ActivityUserFilterData;\n /**\n * Map of subject IDs to names for models display.\n * IMPORTANT: This Map should be memoized with useMemo in the parent component\n * to avoid unnecessary re-fetches when the MODELS tab is active.\n * @example\n * const subjectsMap = useMemo(() => {\n * const map = new Map();\n * subjects.forEach(s => map.set(s.id, s.name));\n * return map;\n * }, [subjects]);\n */\n subjectsMap?: Map<string, string>;\n}\n\n/**\n * ActivitiesHistory component\n * Displays activities history with tabs for history, drafts, and models.\n * Each tab is an independent component that manages its own state and data fetching.\n */\nexport const ActivitiesHistory = ({\n fetchActivitiesHistory,\n fetchActivityModels,\n deleteActivityModel,\n onCreateActivity,\n onCreateModel,\n onRowClick,\n onSendActivity,\n onEditModel,\n emptyStateImage,\n noSearchImage,\n mapSubjectNameToEnum,\n userFilterData,\n subjectsMap,\n}: ActivitiesHistoryProps) => {\n const [activeTab, setActiveTab] = useState<PageTab>(PageTab.HISTORY);\n\n return (\n <div\n data-testid=\"activities-history\"\n className=\"flex flex-col w-full h-auto relative justify-center items-center mb-5 overflow-hidden\"\n >\n {/* Background decoration */}\n <span className=\"absolute top-0 left-0 h-[150px] w-full z-0\" />\n\n {/* Main container */}\n <div className=\"flex flex-col w-full h-full max-w-[1350px] mx-auto z-10 lg:px-0 px-4 pt-4 sm:pt-0\">\n {/* Header Section */}\n <div className=\"flex flex-col sm:flex-row w-full mb-6 items-start sm:items-center sm:justify-between gap-0 sm:gap-4\">\n {/* Page Title */}\n <Text\n as=\"h1\"\n weight=\"bold\"\n className=\"leading-[28px] tracking-[0.2px] text-xl lg:text-2xl\"\n >\n {PAGE_TITLES[activeTab]}\n </Text>\n\n {/* Tabs Menu */}\n <div className=\"flex-shrink-0 lg:w-auto self-center sm:self-auto\">\n <Menu\n defaultValue={PageTab.HISTORY}\n value={activeTab}\n onValueChange={(value: string) => setActiveTab(value as PageTab)}\n variant=\"menu2\"\n className=\"bg-transparent shadow-none px-0\"\n >\n <MenuContent\n variant=\"menu2\"\n className=\"w-full lg:w-auto max-w-full min-w-0\"\n >\n <MenuItem\n variant=\"menu2\"\n value={PageTab.HISTORY}\n data-testid=\"menu-item-history\"\n className=\"whitespace-nowrap flex-1 lg:flex-none\"\n >\n Histórico\n </MenuItem>\n <MenuItem\n variant=\"menu2\"\n value={PageTab.DRAFTS}\n data-testid=\"menu-item-drafts\"\n className=\"whitespace-nowrap flex-1 lg:flex-none\"\n >\n Rascunhos\n </MenuItem>\n <MenuItem\n variant=\"menu2\"\n value={PageTab.MODELS}\n data-testid=\"menu-item-models\"\n className=\"whitespace-nowrap flex-1 lg:flex-none\"\n >\n Modelos\n </MenuItem>\n </MenuContent>\n </Menu>\n </div>\n </div>\n\n {/* Content Area */}\n <div className=\"flex flex-col items-center w-full min-h-0 flex-1\">\n {activeTab === PageTab.HISTORY && (\n <HistoryTab\n fetchActivitiesHistory={fetchActivitiesHistory}\n onCreateActivity={onCreateActivity}\n onRowClick={onRowClick}\n emptyStateImage={emptyStateImage}\n noSearchImage={noSearchImage}\n mapSubjectNameToEnum={mapSubjectNameToEnum}\n userFilterData={userFilterData}\n />\n )}\n\n {activeTab === PageTab.DRAFTS && <DraftsTab />}\n\n {activeTab === PageTab.MODELS && (\n <ModelsTab\n fetchActivityModels={fetchActivityModels}\n deleteActivityModel={deleteActivityModel}\n onCreateModel={onCreateModel}\n onSendActivity={onSendActivity}\n onEditModel={onEditModel}\n emptyStateImage={emptyStateImage}\n noSearchImage={noSearchImage}\n mapSubjectNameToEnum={mapSubjectNameToEnum}\n userFilterData={userFilterData}\n subjectsMap={subjectsMap}\n />\n )}\n </div>\n </div>\n </div>\n );\n};\n\nexport default ActivitiesHistory;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n areFiltersEqual,\n} from './activityFilters';\nexport {\n mapQuestionTypeToEnum,\n mapQuestionTypeToEnumRequired,\n} from './questionTypeUtils';\nexport {\n getStatusBadgeConfig,\n formatTimeSpent,\n formatQuestionNumbers,\n formatDateToBrazilian,\n} from './activityDetailsUtils';\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithoutRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses = '';\n let weightClasses = '';\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={cn(baseClasses, sizeClasses, weightClasses, color, className)}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n","import { create, StoreApi, useStore } from 'zustand';\nimport {\n ReactNode,\n useEffect,\n useRef,\n forwardRef,\n HTMLAttributes,\n KeyboardEvent,\n MouseEvent,\n ReactElement,\n isValidElement,\n Children,\n cloneElement,\n useState,\n} from 'react';\nimport { CaretLeft, CaretRight } from 'phosphor-react';\nimport { cn } from '../../utils/utils';\n\ntype MenuVariant = 'menu' | 'menu2' | 'menu-overflow' | 'breadcrumb';\n\ninterface MenuStore {\n value: string;\n setValue: (value: string) => void;\n onValueChange?: (value: string) => void;\n}\n\ntype MenuStoreApi = StoreApi<MenuStore>;\n\nconst createMenuStore = (\n onValueChange?: (value: string) => void\n): MenuStoreApi =>\n create<MenuStore>((set) => ({\n value: '',\n setValue: (value) => {\n set({ value });\n onValueChange?.(value);\n },\n onValueChange,\n }));\n\nexport const useMenuStore = (externalStore?: MenuStoreApi) => {\n if (!externalStore) throw new Error('MenuItem must be inside Menu');\n return externalStore;\n};\n\ninterface MenuProps extends HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\n defaultValue: string;\n value?: string;\n variant?: MenuVariant;\n onValueChange?: (value: string) => void;\n}\n\nconst VARIANT_CLASSES = {\n menu: 'bg-background shadow-soft-shadow-1 px-6',\n menu2: '',\n 'menu-overflow': '',\n breadcrumb: 'bg-transparent shadow-none !px-0',\n};\n\nconst Menu = forwardRef<HTMLDivElement, MenuProps>(\n (\n {\n className,\n children,\n defaultValue,\n value: propValue,\n variant = 'menu',\n onValueChange,\n ...props\n },\n ref\n ) => {\n const storeRef = useRef<MenuStoreApi>(null);\n storeRef.current ??= createMenuStore(onValueChange);\n const store = storeRef.current;\n const { setValue } = useStore(store, (s) => s);\n\n useEffect(() => {\n setValue(propValue ?? defaultValue);\n }, [defaultValue, propValue, setValue]);\n\n const baseClasses =\n variant === 'menu-overflow'\n ? 'w-fit py-2 flex flex-row items-center justify-center'\n : 'w-full py-2 flex flex-row items-center justify-center';\n const variantClasses = VARIANT_CLASSES[variant];\n\n return (\n <div\n ref={ref}\n className={`\n ${baseClasses}\n ${variantClasses}\n ${className ?? ''}\n `}\n {...props}\n >\n {injectStore(children, store)}\n </div>\n );\n }\n);\nMenu.displayName = 'Menu';\n\ninterface MenuContentProps extends HTMLAttributes<HTMLUListElement> {\n children: ReactNode;\n variant?: MenuVariant;\n}\n\nconst MenuContent = forwardRef<HTMLUListElement, MenuContentProps>(\n ({ className, children, variant = 'menu', ...props }, ref) => {\n const baseClasses = 'w-full flex flex-row items-center gap-2';\n\n const variantClasses =\n variant === 'menu2' || variant === 'menu-overflow'\n ? 'overflow-x-auto scroll-smooth'\n : '';\n\n return (\n <ul\n ref={ref}\n className={`\n ${baseClasses}\n ${variantClasses}\n ${variant == 'breadcrumb' ? 'flex-wrap' : ''}\n ${className ?? ''}\n `}\n style={\n variant === 'menu2' || variant === 'menu-overflow'\n ? { scrollbarWidth: 'none', msOverflowStyle: 'none' }\n : undefined\n }\n {...props}\n >\n {children}\n </ul>\n );\n }\n);\nMenuContent.displayName = 'MenuContent';\n\ninterface MenuItemProps extends HTMLAttributes<HTMLLIElement> {\n value: string;\n disabled?: boolean;\n store?: MenuStoreApi;\n variant?: MenuVariant;\n separator?: boolean;\n}\n\nconst MenuItem = forwardRef<HTMLLIElement, MenuItemProps>(\n (\n {\n className,\n children,\n value,\n disabled = false,\n store: externalStore,\n variant = 'menu',\n separator = false,\n ...props\n },\n ref\n ) => {\n const store = useMenuStore(externalStore);\n const { value: selectedValue, setValue } = useStore(store, (s) => s);\n\n const handleClick = (\n e: MouseEvent<HTMLLIElement> | KeyboardEvent<HTMLLIElement>\n ) => {\n if (!disabled) {\n setValue(value);\n }\n props.onClick?.(e as MouseEvent<HTMLLIElement>);\n };\n\n const commonProps = {\n role: 'menuitem',\n 'aria-disabled': disabled,\n ref,\n onClick: handleClick,\n onKeyDown: (e: KeyboardEvent<HTMLLIElement>) => {\n if (['Enter', ' '].includes(e.key)) handleClick(e);\n },\n tabIndex: disabled ? -1 : 0,\n onMouseDown: (e: MouseEvent<HTMLLIElement>) => {\n e.preventDefault();\n },\n ...props,\n };\n\n const variants: Record<string, ReactNode> = {\n menu: (\n <li\n data-variant=\"menu\"\n className={`\n w-full flex flex-col items-center justify-center gap-0.5 py-1 px-2 rounded-sm font-medium text-xs\n [&>svg]:size-6 cursor-pointer hover:bg-primary-600 hover:text-text\n focus:outline-none focus:border-indicator-info focus:border-2\n ${selectedValue === value ? 'bg-primary-50 text-primary-950' : 'text-text-950'}\n ${className ?? ''}\n `}\n {...commonProps}\n >\n {children}\n </li>\n ),\n menu2: (\n <li\n data-variant=\"menu2\"\n className={`\n w-full flex flex-col items-center px-2 pt-4 gap-3 cursor-pointer focus:rounded-sm justify-center hover:bg-background-100 rounded-lg\n focus:outline-none focus:border-indicator-info focus:border-2\n ${selectedValue === value ? '' : 'pb-4'}\n `}\n {...commonProps}\n >\n <span\n className={cn(\n 'flex flex-row items-center gap-2 px-4 text-text-950 text-xs font-bold',\n className\n )}\n >\n {children}\n </span>\n {selectedValue === value && (\n <div className=\"h-1 w-full bg-primary-950 rounded-lg\" />\n )}\n </li>\n ),\n 'menu-overflow': (\n <li\n data-variant=\"menu-overflow\"\n className={`\n w-fit flex flex-col items-center px-2 pt-4 gap-3 cursor-pointer focus:rounded-sm justify-center hover:bg-background-100 rounded-lg\n focus:outline-none focus:border-indicator-info focus:border-2\n ${selectedValue === value ? '' : 'pb-4'}\n `}\n {...commonProps}\n >\n <span\n className={cn(\n 'flex flex-row items-center gap-2 px-4 text-text-950 text-xs font-bold',\n className\n )}\n >\n {children}\n </span>\n {selectedValue === value && (\n <div className=\"h-1 w-full bg-primary-950 rounded-lg\" />\n )}\n </li>\n ),\n breadcrumb: (\n <li\n data-variant=\"breadcrumb\"\n className={`\n flex flex-row gap-2 items-center w-fit p-2 rounded-lg hover:text-primary-600 cursor-pointer font-bold text-xs\n focus:outline-none focus:border-indicator-info focus:border-2\n ${selectedValue === value ? 'text-text-950' : 'text-text-600'}\n ${className ?? ''}\n `}\n {...commonProps}\n >\n <span\n className={cn(\n 'border-b border-text-600 hover:border-primary-600 text-inherit text-xs',\n selectedValue === value\n ? 'border-b-0 font-bold'\n : 'border-b-text-600'\n )}\n >\n {children}\n </span>\n\n {separator && (\n <CaretRight\n size={16}\n className=\"text-text-600\"\n data-testid=\"separator\"\n />\n )}\n </li>\n ),\n };\n\n return variants[variant] ?? variants['menu'];\n }\n);\nMenuItem.displayName = 'MenuItem';\n\nconst MenuItemIcon = ({\n className,\n icon,\n ...props\n}: HTMLAttributes<HTMLSpanElement> & { icon: ReactNode }) => (\n <span\n className={cn(\n 'bg-background-500 w-[21px] h-[21px] flex items-center justify-center [&>svg]:w-[17px] [&>svg]:h-[17px] rounded-sm',\n className\n )}\n {...props}\n >\n {icon}\n </span>\n);\n\nexport const internalScroll = (\n container: HTMLUListElement | null,\n direction: 'left' | 'right'\n) => {\n if (!container) return;\n container.scrollBy({\n left: direction === 'left' ? -150 : 150,\n behavior: 'smooth',\n });\n};\n\nexport const internalCheckScroll = (\n container: HTMLUListElement | null,\n setShowLeftArrow: (v: boolean) => void,\n setShowRightArrow: (v: boolean) => void\n) => {\n if (!container) return;\n const { scrollLeft, scrollWidth, clientWidth } = container;\n setShowLeftArrow(scrollLeft > 0);\n setShowRightArrow(scrollLeft + clientWidth < scrollWidth);\n};\n\ninterface MenuOverflowProps extends HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\n defaultValue: string;\n value?: string;\n onValueChange?: (value: string) => void;\n}\n\nconst MenuOverflow = ({\n children,\n className,\n defaultValue,\n value,\n onValueChange,\n ...props\n}: MenuOverflowProps) => {\n const containerRef = useRef<HTMLUListElement>(null);\n const [showLeftArrow, setShowLeftArrow] = useState(false);\n const [showRightArrow, setShowRightArrow] = useState(false);\n\n useEffect(() => {\n const checkScroll = () =>\n internalCheckScroll(\n containerRef.current,\n setShowLeftArrow,\n setShowRightArrow\n );\n checkScroll();\n const container = containerRef.current;\n container?.addEventListener('scroll', checkScroll);\n window.addEventListener('resize', checkScroll);\n return () => {\n container?.removeEventListener('scroll', checkScroll);\n window.removeEventListener('resize', checkScroll);\n };\n }, []);\n\n return (\n <div\n data-testid=\"menu-overflow-wrapper\"\n className={cn('relative w-full overflow-hidden', className)}\n >\n {showLeftArrow && (\n <button\n onClick={() => internalScroll(containerRef.current, 'left')}\n className=\"absolute left-0 top-1/2 -translate-y-1/2 z-10 flex h-8 w-8 items-center justify-center rounded-full bg-white shadow-md cursor-pointer\"\n data-testid=\"scroll-left-button\"\n >\n <CaretLeft size={16} />\n <span className=\"sr-only\">Scroll left</span>\n </button>\n )}\n\n <Menu\n defaultValue={defaultValue}\n onValueChange={onValueChange}\n value={value}\n variant=\"menu2\"\n {...props}\n >\n <MenuContent ref={containerRef} variant=\"menu2\">\n {children}\n </MenuContent>\n </Menu>\n\n {showRightArrow && (\n <button\n onClick={() => internalScroll(containerRef.current, 'right')}\n className=\"absolute right-0 top-1/2 -translate-y-1/2 z-10 flex h-8 w-8 items-center justify-center rounded-full bg-white shadow-md cursor-pointer\"\n data-testid=\"scroll-right-button\"\n >\n <CaretRight size={16} />\n <span className=\"sr-only\">Scroll right</span>\n </button>\n )}\n </div>\n );\n};\n\nconst injectStore = (children: ReactNode, store: MenuStoreApi): ReactNode =>\n Children.map(children, (child) => {\n if (!isValidElement(child)) return child;\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n const typedChild = child as ReactElement<any>;\n const shouldInject = typedChild.type === MenuItem;\n return cloneElement(typedChild, {\n ...(shouldInject ? { store } : {}),\n ...(typedChild.props.children\n ? { children: injectStore(typedChild.props.children, store) }\n : {}),\n });\n });\n\nexport default Menu;\nexport { Menu, MenuContent, MenuItem, MenuOverflow, MenuItemIcon };\n","import type { ReactNode } from 'react';\nimport { useCallback, useMemo, useRef } from 'react';\nimport { Plus } from 'phosphor-react';\nimport Button from '../../Button/Button';\nimport EmptyState from '../../EmptyState/EmptyState';\nimport { TableProvider } from '../../TableProvider/TableProvider';\nimport { ErrorDisplay } from '../components/ErrorDisplay';\nimport { createHistoryTableColumns } from '../config/historyTableColumns';\nimport { createHistoryFiltersConfig } from '../config/historyFiltersConfig';\nimport { buildHistoryFiltersFromParams } from '../utils/filterBuilders';\nimport {\n createUseActivitiesHistory,\n type UseActivitiesHistoryReturn,\n} from '../../../hooks/useActivitiesHistory';\nimport type { TableParams } from '../../TableProvider/TableProvider';\nimport type {\n ActivityTableItem,\n ActivityHistoryFilters,\n ActivitiesHistoryApiResponse,\n ActivityUserFilterData,\n} from '../../../types/activitiesHistory';\nimport type { SubjectEnum } from '../../../enums/SubjectEnum';\n\n/**\n * Props for the HistoryTab component\n */\nexport interface HistoryTabProps {\n /** Function to fetch activities history from API */\n fetchActivitiesHistory: (\n filters?: ActivityHistoryFilters\n ) => Promise<ActivitiesHistoryApiResponse>;\n /** Callback when create activity button is clicked */\n onCreateActivity: () => void;\n /** Callback when a history row is clicked */\n onRowClick: (row: ActivityTableItem) => void;\n /** Image for empty state */\n emptyStateImage?: string;\n /** Image for no search results */\n noSearchImage?: string;\n /** Function to map subject name to SubjectEnum */\n mapSubjectNameToEnum?: (subjectName: string) => SubjectEnum | null;\n /** User data for populating filter options */\n userFilterData?: ActivityUserFilterData;\n}\n\n/**\n * HistoryTab component\n * Independent component for displaying activities history with filters and pagination\n */\nexport const HistoryTab = ({\n fetchActivitiesHistory,\n onCreateActivity,\n onRowClick,\n emptyStateImage,\n noSearchImage,\n mapSubjectNameToEnum,\n userFilterData,\n}: HistoryTabProps) => {\n // Use ref to keep stable reference to fetch function\n const fetchActivitiesHistoryRef = useRef(fetchActivitiesHistory);\n fetchActivitiesHistoryRef.current = fetchActivitiesHistory;\n\n // Create hook instance with stable fetch function wrapper\n const useActivitiesHistory = useMemo(\n () =>\n createUseActivitiesHistory((filters) =>\n fetchActivitiesHistoryRef.current(filters)\n ),\n []\n );\n\n // Use the hook\n const {\n activities,\n loading,\n error,\n pagination,\n fetchActivities,\n }: UseActivitiesHistoryReturn = useActivitiesHistory();\n\n // Create filter and column configurations\n const historyFilterConfigs = useMemo(\n () => createHistoryFiltersConfig(userFilterData),\n [userFilterData]\n );\n\n const historyTableColumns = useMemo(\n () => createHistoryTableColumns(mapSubjectNameToEnum),\n [mapSubjectNameToEnum]\n );\n\n /**\n * Handle table params change\n */\n const handleParamsChange = useCallback(\n (params: TableParams) => {\n const filters = buildHistoryFiltersFromParams(params);\n fetchActivities(filters);\n },\n [fetchActivities]\n );\n\n // Error State\n if (error) {\n return <ErrorDisplay error={error} />;\n }\n\n return (\n <div className=\"w-full\">\n <TableProvider\n data={activities}\n headers={historyTableColumns}\n loading={loading}\n variant=\"borderless\"\n enableSearch\n enableFilters\n enableTableSort\n enablePagination\n enableRowClick\n initialFilters={historyFilterConfigs}\n paginationConfig={{\n itemLabel: 'atividades',\n itemsPerPageOptions: [10, 20, 50, 100],\n defaultItemsPerPage: 10,\n totalItems: pagination.total,\n totalPages: pagination.totalPages,\n }}\n searchPlaceholder=\"Buscar atividade\"\n noSearchResultState={{\n image: noSearchImage,\n }}\n emptyState={{\n component: (\n <EmptyState\n image={emptyStateImage}\n title=\"Incentive sua turma ao aprendizado\"\n description=\"Crie uma nova atividade e ajude seus alunos a colocarem o conteúdo em prática!\"\n buttonText=\"Criar atividade\"\n buttonIcon={<Plus size={18} />}\n buttonVariant=\"outline\"\n buttonAction=\"primary\"\n onButtonClick={onCreateActivity}\n />\n ),\n }}\n onParamsChange={handleParamsChange}\n onRowClick={onRowClick}\n >\n {(renderProps: unknown) => {\n const {\n controls,\n table,\n pagination: paginationComponent,\n } = renderProps as {\n controls: ReactNode;\n table: ReactNode;\n pagination: ReactNode;\n };\n return (\n <div className=\"space-y-4\">\n {/* Header row: Button on left, Controls on right */}\n <div className=\"flex items-center justify-between gap-4\">\n <Button\n variant=\"solid\"\n action=\"primary\"\n size=\"medium\"\n onClick={onCreateActivity}\n iconLeft={<Plus size={18} weight=\"bold\" />}\n >\n Criar atividade\n </Button>\n {controls}\n </div>\n {/* Table and pagination */}\n <div className=\"bg-background rounded-xl p-6 space-y-4\">\n {table}\n {paginationComponent}\n </div>\n </div>\n );\n }}\n </TableProvider>\n </div>\n );\n};\n\nexport default HistoryTab;\n","import { ButtonHTMLAttributes, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Lookup table for variant and action class combinations\n */\nconst VARIANT_ACTION_CLASSES = {\n solid: {\n primary:\n 'bg-primary-950 text-text border border-primary-950 hover:bg-primary-800 hover:border-primary-800 focus-visible:outline-none focus-visible:bg-primary-950 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-primary-700 active:border-primary-700 disabled:bg-primary-500 disabled:border-primary-500 disabled:opacity-40 disabled:cursor-not-allowed',\n positive:\n 'bg-success-500 text-text border border-success-500 hover:bg-success-600 hover:border-success-600 focus-visible:outline-none focus-visible:bg-success-500 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-success-700 active:border-success-700 disabled:bg-success-500 disabled:border-success-500 disabled:opacity-40 disabled:cursor-not-allowed',\n negative:\n 'bg-error-500 text-text border border-error-500 hover:bg-error-600 hover:border-error-600 focus-visible:outline-none focus-visible:bg-error-500 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-error-700 active:border-error-700 disabled:bg-error-500 disabled:border-error-500 disabled:opacity-40 disabled:cursor-not-allowed',\n },\n outline: {\n primary:\n 'bg-transparent text-primary-950 border border-primary-950 hover:bg-background-50 hover:text-primary-400 hover:border-primary-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-primary-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-primary-700 active:border-primary-700 disabled:opacity-40 disabled:cursor-not-allowed',\n positive:\n 'bg-transparent text-success-500 border border-success-300 hover:bg-background-50 hover:text-success-400 hover:border-success-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-success-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-success-700 active:border-success-700 disabled:opacity-40 disabled:cursor-not-allowed',\n negative:\n 'bg-transparent text-error-500 border border-error-300 hover:bg-background-50 hover:text-error-400 hover:border-error-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-error-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-error-700 active:border-error-700 disabled:opacity-40 disabled:cursor-not-allowed',\n },\n link: {\n primary:\n 'bg-transparent text-primary-950 hover:text-primary-400 focus-visible:outline-none focus-visible:text-primary-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-primary-700 disabled:opacity-40 disabled:cursor-not-allowed',\n positive:\n 'bg-transparent text-success-500 hover:text-success-400 focus-visible:outline-none focus-visible:text-success-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-success-700 disabled:opacity-40 disabled:cursor-not-allowed',\n negative:\n 'bg-transparent text-error-500 hover:text-error-400 focus-visible:outline-none focus-visible:text-error-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-error-700 disabled:opacity-40 disabled:cursor-not-allowed',\n },\n} as const;\n\n/**\n * Lookup table for size classes\n */\nconst SIZE_CLASSES = {\n 'extra-small': 'text-xs px-3.5 py-2',\n small: 'text-sm px-4 py-2.5',\n medium: 'text-md px-5 py-2.5',\n large: 'text-lg px-6 py-3',\n 'extra-large': 'text-lg px-7 py-3.5',\n} as const;\n\n/**\n * Button component props interface\n */\ntype ButtonProps = {\n /** Content to be displayed inside the button */\n children: ReactNode;\n /** Ícone à esquerda do texto */\n iconLeft?: ReactNode;\n /** Ícone à direita do texto */\n iconRight?: ReactNode;\n /** Size of the button */\n size?: 'extra-small' | 'small' | 'medium' | 'large' | 'extra-large';\n /** Visual variant of the button */\n variant?: 'solid' | 'outline' | 'link';\n /** Action type of the button */\n action?: 'primary' | 'positive' | 'negative';\n /** Additional CSS classes to apply */\n className?: string;\n} & ButtonHTMLAttributes<HTMLButtonElement>;\n\n/**\n * Button component for Analytica Ensino platforms\n *\n * A flexible button component with multiple variants, sizes and actions.\n *\n * @param children - The content to display inside the button\n * @param size - The size variant (extra-small, small, medium, large, extra-large)\n * @param variant - The visual style variant (solid, outline, link)\n * @param action - The action type (primary, positive, negative)\n * @param className - Additional CSS classes\n * @param props - All other standard button HTML attributes\n * @returns A styled button element\n *\n * @example\n * ```tsx\n * <Button variant=\"solid\" action=\"primary\" size=\"medium\" onClick={() => console.log('clicked')}>\n * Click me\n * </Button>\n * ```\n */\nconst Button = ({\n children,\n iconLeft,\n iconRight,\n size = 'medium',\n variant = 'solid',\n action = 'primary',\n className = '',\n disabled,\n type = 'button',\n ...props\n}: ButtonProps) => {\n // Get classes from lookup tables\n const sizeClasses = SIZE_CLASSES[size];\n const variantClasses = VARIANT_ACTION_CLASSES[variant][action];\n\n const baseClasses =\n 'inline-flex items-center justify-center rounded-full cursor-pointer font-medium';\n\n return (\n <button\n className={cn(baseClasses, variantClasses, sizeClasses, className)}\n disabled={disabled}\n type={type}\n {...props}\n >\n {iconLeft && <span className=\"mr-2 flex items-center\">{iconLeft}</span>}\n {children}\n {iconRight && <span className=\"ml-2 flex items-center\">{iconRight}</span>}\n </button>\n );\n};\n\nexport default Button;\n","import { type ReactNode } from 'react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\n\nexport interface EmptyStateProps {\n /**\n * Image source for the illustration (optional)\n */\n image?: string;\n /**\n * Title text to display\n * @default \"Nenhum dado disponível\"\n */\n title?: string;\n /**\n * Description text to display below the title\n * @default \"Não há dados para exibir no momento.\"\n */\n description?: string;\n /**\n * Button text (optional - if not provided, button won't be displayed)\n */\n buttonText?: string;\n /**\n * Icon to display on the left side of the button\n */\n buttonIcon?: ReactNode;\n /**\n * Callback function when button is clicked\n */\n onButtonClick?: () => void;\n /**\n * Button variant\n * @default \"solid\"\n */\n buttonVariant?: 'solid' | 'outline' | 'link';\n /**\n * Button action color\n * @default \"primary\"\n */\n buttonAction?: 'primary' | 'positive' | 'negative';\n}\n\n/**\n * Component displayed when there is no data to show (empty state)\n * Shows an illustration with customizable title, description, and optional button in horizontal layout\n *\n * @example\n * ```tsx\n * import { EmptyState } from 'analytica-frontend-lib';\n * import activityImage from './assets/activity.png';\n * import { Plus } from 'phosphor-react';\n *\n * <EmptyState\n * image={activityImage}\n * title=\"Incentive sua turma ao aprendizado\"\n * description=\"Crie uma nova atividade e ajude seus alunos a colocarem o conteúdo em prática!\"\n * buttonText=\"Criar atividade\"\n * buttonIcon={<Plus size={18} />}\n * buttonVariant=\"outline\"\n * onButtonClick={handleCreateActivity}\n * />\n * ```\n */\nconst EmptyState = ({\n image,\n title,\n description,\n buttonText,\n buttonIcon,\n onButtonClick,\n buttonVariant = 'solid',\n buttonAction = 'primary',\n}: EmptyStateProps) => {\n const displayTitle = title || 'Nenhum dado disponível';\n const displayDescription =\n description || 'Não há dados para exibir no momento.';\n\n return (\n <div className=\"flex flex-col justify-center items-center gap-6 w-full min-h-[705px] bg-background rounded-xl p-6\">\n {/* Illustration */}\n {image && (\n <img src={image} alt={displayTitle} className=\"w-[170px] h-[150px]\" />\n )}\n\n {/* Text Content Container */}\n <div className=\"flex flex-col items-center gap-4 w-full max-w-[600px] px-6\">\n {/* Title */}\n <Text\n as=\"h2\"\n className=\"text-text-950 font-semibold text-3xl leading-[35px] text-center\"\n >\n {displayTitle}\n </Text>\n\n {/* Description */}\n <Text className=\"text-text-600 font-normal text-[18px] leading-[27px] text-center\">\n {displayDescription}\n </Text>\n </div>\n\n {/* Button */}\n {buttonText && onButtonClick && (\n <Button\n variant={buttonVariant}\n action={buttonAction}\n size=\"large\"\n onClick={onButtonClick}\n iconLeft={buttonIcon}\n className=\"rounded-full px-5 py-2.5\"\n >\n {buttonText}\n </Button>\n )}\n </div>\n );\n};\n\nexport default EmptyState;\n","import { useState, useEffect, useMemo, useCallback, ReactNode } from 'react';\nimport Table, {\n TableBody,\n TableHead,\n TableRow,\n TableCell,\n useTableSort,\n TablePagination,\n} from '../Table/Table';\nimport { useTableFilter, FilterConfig } from '../Filter/useTableFilter';\nimport Search from '../Search/Search';\nimport { FilterModal } from '../Filter/FilterModal';\nimport Button from '../Button/Button';\nimport { Funnel } from 'phosphor-react';\n\n/**\n * Column configuration with flexible rendering options\n */\nexport interface ColumnConfig<T = Record<string, unknown>> {\n /** Column key (must match data object key) */\n key: string;\n /** Column label - can be string or JSX */\n label: string | ReactNode;\n /** Enable sorting for this column */\n sortable?: boolean;\n /** Custom render function for cell content */\n render?: (value: unknown, row: T, index: number) => ReactNode;\n /** Column width */\n width?: string;\n /** Additional CSS classes */\n className?: string;\n /** Text alignment */\n align?: 'left' | 'center' | 'right';\n}\n\n/**\n * Combined parameters sent via onParamsChange\n */\nexport interface TableParams {\n /** Current page number */\n page: number;\n /** Items per page */\n limit: number;\n /** Search query */\n search?: string;\n /** Active filters (dynamic keys based on filter configs) */\n [key: string]: unknown;\n /** Sort column */\n sortBy?: string;\n /** Sort direction */\n sortOrder?: 'asc' | 'desc';\n}\n\n/**\n * Pagination configuration\n */\nexport interface PaginationConfig {\n /** Label for items (e.g., \"atividades\") */\n itemLabel?: string;\n /** Items per page options */\n itemsPerPageOptions?: number[];\n /** Default items per page */\n defaultItemsPerPage?: number;\n /** Total items (for displaying pagination info) */\n totalItems?: number;\n /** Total pages (if known from backend) */\n totalPages?: number;\n}\n\n/**\n * Empty state configuration\n */\nexport interface EmptyStateConfig {\n /** Custom component to render when table is empty (no data and no search active) */\n component?: ReactNode;\n /** Image to display in empty state (path from project) */\n image?: string;\n /** Title text for empty state */\n title?: string;\n /** Description text for empty state */\n description?: string;\n /** Button text for empty state action (optional) */\n buttonText?: string;\n /** Icon to display on button (optional) */\n buttonIcon?: ReactNode;\n /** Callback when empty state button is clicked */\n onButtonClick?: () => void;\n /** Button variant (solid, outline, or link) */\n buttonVariant?: 'solid' | 'outline' | 'link';\n /** Button action color (primary, positive, or negative) */\n buttonAction?: 'primary' | 'positive' | 'negative';\n}\n\n/**\n * Loading state configuration\n */\nexport interface LoadingStateConfig {\n /** Custom component to render when table is loading */\n component?: ReactNode;\n}\n\n/**\n * No search result state configuration\n */\nexport interface NoSearchResultConfig {\n /** Custom component to render when search returns no results */\n component?: ReactNode;\n /** Title for no search result state */\n title?: string;\n /** Description for no search result state */\n description?: string;\n /** Image to display in no search result state */\n image?: string;\n}\n\n/**\n * Table components exposed via render prop\n */\nexport interface TableComponents {\n /** Search and filter controls */\n controls: ReactNode;\n /** Table with data */\n table: ReactNode;\n /** Pagination controls */\n pagination: ReactNode;\n}\n\n/**\n * TableProvider Props\n */\nexport interface TableProviderProps<T = Record<string, unknown>> {\n /** Data to display in the table */\n readonly data: T[];\n /** Column configurations */\n readonly headers: ColumnConfig<T>[];\n /** Loading state */\n readonly loading?: boolean;\n /** Table variant */\n readonly variant?: 'default' | 'borderless';\n\n /** Enable search functionality */\n readonly enableSearch?: boolean;\n /** Enable filters functionality */\n readonly enableFilters?: boolean;\n /** Enable table sorting */\n readonly enableTableSort?: boolean;\n /** Enable pagination */\n readonly enablePagination?: boolean;\n /** Enable row click functionality */\n readonly enableRowClick?: boolean;\n\n /** Initial filter configurations */\n readonly initialFilters?: FilterConfig[];\n /** Pagination configuration */\n readonly paginationConfig?: PaginationConfig;\n /** Search placeholder text */\n readonly searchPlaceholder?: string;\n /** Empty state configuration (when table is empty with no search) */\n readonly emptyState?: EmptyStateConfig;\n /** Loading state configuration (when table is loading) */\n readonly loadingState?: LoadingStateConfig;\n /** No search result state configuration (when search returns no results) */\n readonly noSearchResultState?: NoSearchResultConfig;\n /** Key field name to use for unique row identification (recommended for better performance) */\n readonly rowKey?: keyof T;\n\n /** Callback when any parameter changes */\n readonly onParamsChange?: (params: TableParams) => void;\n /** Callback when row is clicked */\n readonly onRowClick?: (row: T, index: number) => void;\n\n /**\n * Render prop for custom layout control\n * When provided, gives full control over component positioning\n * @param components - Table components (controls, table, pagination)\n * @returns Custom layout JSX\n *\n * @example\n * ```tsx\n * <TableProvider {...props}>\n * {({ controls, table, pagination }) => (\n * <>\n * <div className=\"mb-4\">{controls}</div>\n * <div className=\"bg-white p-6\">\n * {table}\n * {pagination}\n * </div>\n * </>\n * )}\n * </TableProvider>\n * ```\n */\n readonly children?: (components: TableComponents) => ReactNode;\n}\n\n/**\n * TableProvider - Self-contained table component with search, filters, sorting, and pagination\n *\n * @example\n * ```tsx\n * <TableProvider\n * data={activities}\n * headers={[\n * { key: 'title', label: 'Título', sortable: true },\n * { key: 'status', label: 'Status', render: (value) => <Badge>{value}</Badge> }\n * ]}\n * loading={loading}\n * variant=\"borderless\"\n * enableSearch\n * enableFilters\n * enableTableSort\n * enablePagination\n * enableRowClick\n * initialFilters={filterConfigs}\n * paginationConfig={{ itemLabel: 'atividades' }}\n * onParamsChange={handleParamsChange}\n * onRowClick={handleRowClick}\n * />\n * ```\n */\nexport function TableProvider<T extends Record<string, unknown>>({\n data,\n headers,\n loading = false,\n variant = 'default',\n enableSearch = false,\n enableFilters = false,\n enableTableSort = false,\n enablePagination = false,\n enableRowClick = false,\n initialFilters = [],\n paginationConfig = {},\n searchPlaceholder = 'Buscar...',\n emptyState,\n loadingState,\n noSearchResultState,\n rowKey,