Skip to content
1 change: 1 addition & 0 deletions src/components/chatbot/ChatbotNodeDialogs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from 'lucide-react';
import { cn } from '@/lib/utils';

// eslint-disable-next-line react-refresh/only-export-components
export const nodeTypes: Record<string, { label: string; icon: React.ComponentType<{ className?: string }>; color: string }> = {
start: { label: 'Início', icon: Zap, color: 'border-success bg-success/10' },
message: { label: 'Mensagem', icon: MessageSquare, color: 'border-info bg-info/10' },
Expand Down
1 change: 1 addition & 0 deletions src/components/cognitive/ProgressiveDisclosure.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface DisclosureContextType {

const DisclosureContext = createContext<DisclosureContextType>({ level: 'basic', setLevel: () => {} });

// eslint-disable-next-line react-refresh/only-export-components
export function useDisclosureLevel() { return useContext(DisclosureContext); }

export function DisclosureProvider({ children }: { children: ReactNode }) {
Expand Down
1 change: 1 addition & 0 deletions src/components/contacts/ContactEngagementScore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,5 @@ export function ContactEngagementScore({
);
}

// eslint-disable-next-line react-refresh/only-export-components
export { calculateEngagement };
1 change: 1 addition & 0 deletions src/components/contacts/ContactErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export class ContactErrorBoundary extends Component<Props, State> {
* Wrap any contacts component with an error boundary.
* Usage: const SafeContactsView = withContactErrorBoundary(ContactsViewV3);
*/
// eslint-disable-next-line react-refresh/only-export-components
export function withContactErrorBoundary<T extends object>(
Component: React.ComponentType<T>,
onReset?: () => void
Expand Down
1 change: 1 addition & 0 deletions src/components/contacts/ContactsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const CONTACT_TYPE_ICONS: Record<string, React.ReactNode> = {
outros: <MoreHorizontal className="w-4 h-4" />,
};

// eslint-disable-next-line react-refresh/only-export-components
export { CONTACT_TYPE_ICONS };

type SortField = 'name' | 'type' | 'phone' | 'email' | 'company' | 'job_title' | 'created_at';
Expand Down
1 change: 1 addition & 0 deletions src/components/dashboard/DashboardFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const PERIOD_OPTIONS = [
{ value: 'custom', label: 'Personalizado' },
] as const;

// eslint-disable-next-line react-refresh/only-export-components
export const getDefaultFilters = (): DashboardFiltersState => ({
dateRange: {
from: startOfDay(new Date()),
Expand Down
1 change: 1 addition & 0 deletions src/components/dashboard/DashboardWidgetRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ interface DashboardStats {
}>;
}

// eslint-disable-next-line react-refresh/only-export-components
export function buildStatsCards(stats: DashboardStats) {
const openRate = stats.totalConversations > 0
? Math.round((stats.openConversations / stats.totalConversations) * 100)
Expand Down
1 change: 1 addition & 0 deletions src/components/dashboard/SentimentHelpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface SentimentData {
alerts_count: number;
}

// eslint-disable-next-line react-refresh/only-export-components
export function useRealSentimentData(days: number): SentimentData[] | null {
const { data } = useQuery({
queryKey: ['sentiment-trend', days],
Expand Down
1 change: 1 addition & 0 deletions src/components/dashboard/TrendIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface TrendIndicatorProps {
animated?: boolean;
}

// eslint-disable-next-line react-refresh/only-export-components
export function calculateTrend(current: number, previous: number): {
percentage: number;
direction: 'up' | 'down' | 'neutral';
Expand Down
1 change: 1 addition & 0 deletions src/components/dashboard/WidgetConfigSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,5 @@ export function WidgetConfigSheet({ widgets, isEditMode, setIsEditMode, onToggle
);
}

// eslint-disable-next-line react-refresh/only-export-components
export { sizeLabels, sizeIcons };
1 change: 1 addition & 0 deletions src/components/effects/Confetti.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ export function CelebrationOverlay({
}

// Hook for triggering celebrations
// eslint-disable-next-line react-refresh/only-export-components
export function useCelebration() {
const [celebrating, setCelebrating] = useState(false);
const [celebrationData, setCelebrationData] = useState<{
Expand Down
1 change: 1 addition & 0 deletions src/components/effects/EasterEggs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ export const EasterEggsProvider = forwardRef<HTMLDivElement, EasterEggsProviderP
});

// Hook to trigger easter eggs programmatically
// eslint-disable-next-line react-refresh/only-export-components
export function useEasterEggs() {
const { celebrate } = useCelebration();

Expand Down
1 change: 1 addition & 0 deletions src/components/errors/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ export class ErrorBoundary extends Component<Props, State> {
}

// HOC para envolver componentes com Error Boundary
// eslint-disable-next-line react-refresh/only-export-components
export function withErrorBoundary<P extends object>(
WrappedComponent: React.ComponentType<P>,
fallback?: ReactNode
Expand Down
1 change: 1 addition & 0 deletions src/components/keyboard/GlobalKeyboardProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface GlobalKeyboardContextType {

const GlobalKeyboardContext = createContext<GlobalKeyboardContextType | null>(null);

// eslint-disable-next-line react-refresh/only-export-components
export const useGlobalKeyboard = () => {
const context = useContext(GlobalKeyboardContext);
if (!context) {
Expand Down
1 change: 1 addition & 0 deletions src/components/mobile/InAppNotificationProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const InAppNotificationContext = createContext<InAppNotificationContextType>({
showNotification: () => {},
});

// eslint-disable-next-line react-refresh/only-export-components
export function useInAppNotification() {
return useContext(InAppNotificationContext);
}
Expand Down
1 change: 1 addition & 0 deletions src/components/mobile/MobileNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export function MobileTabBar({ items, activeId, onChange, className, variant = '
}

// Default tab presets
// eslint-disable-next-line react-refresh/only-export-components
export const defaultMobileTabItems: TabItem[] = [
{ id: 'home', icon: <Home className="w-5 h-5" />, label: 'Início' },
{ id: 'inbox', icon: <MessageSquare className="w-5 h-5" />, label: 'Inbox', badge: 3 },
Expand Down
1 change: 1 addition & 0 deletions src/components/mobile/SwipeGestures.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { motion, useMotionValue, useTransform, PanInfo, AnimatePresence } from '
import { cn } from '@/lib/utils';

// Re-export utilities and types from extracted modules
// eslint-disable-next-line react-refresh/only-export-components
export { haptics, defaultSwipeActions } from './swipeUtils';
export type { SwipeAction } from './swipeUtils';
export { TouchRipple } from './TouchRipple';
Expand Down
2 changes: 2 additions & 0 deletions src/components/onboarding/OnboardingTour.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ interface TourContextType {

const TourContext = createContext<TourContextType | null>(null);

// eslint-disable-next-line react-refresh/only-export-components
export function useTour() {
const context = useContext(TourContext);
if (!context) {
Expand Down Expand Up @@ -83,4 +84,5 @@ export function TourProvider({ children, onComplete }: TourProviderProps) {
}

// Re-export for backward compatibility
// eslint-disable-next-line react-refresh/only-export-components
export { DEFAULT_ONBOARDING_STEPS } from './defaultTourSteps';
2 changes: 1 addition & 1 deletion src/components/performance/LazyRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export function LazyView({ children, fallbackType = 'default' }: LazyViewProps)
}

// HOC for creating lazy components with custom fallback
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- generic HOC requires any for ComponentType inference
// eslint-disable-next-line @typescript-eslint/no-explicit-any, react-refresh/only-export-components -- generic HOC requires any for ComponentType inference
export function withLazyLoading<T extends ComponentType<any>>(
importFn: () => Promise<{ default: T }>,
fallbackType: LazyLoadFallbackProps['type'] = 'default'
Expand Down
65 changes: 54 additions & 11 deletions src/components/performance/Prefetcher.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable react-refresh/only-export-components -- componente exporta múltiplos símbolos não-componente (hooks, constants, helpers); split em arquivos próprios fica em backlog */
import { useEffect, useCallback } from 'react';
import { useQueryClient } from '@tanstack/react-query';

Expand Down Expand Up @@ -74,14 +75,30 @@ export function useIntersectionPrefetch(routes: string[]) {
const { prefetch } = usePrefetchRoute();

useEffect(() => {
// Prefetch routes when user is idle
const idleCallback = window.requestIdleCallback || ((cb) => setTimeout(cb, 1));
let idleHandle: number | undefined;
let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
const hasIdleCallback = typeof window !== 'undefined' && 'requestIdleCallback' in window;

idleCallback(() => {
const run = () => {
routes.forEach((route) => {
prefetch(route);
});
});
};

if (hasIdleCallback) {
idleHandle = window.requestIdleCallback(run);
} else {
timeoutHandle = setTimeout(run, 1);
}

return () => {
if (idleHandle !== undefined && hasIdleCallback && 'cancelIdleCallback' in window) {
window.cancelIdleCallback(idleHandle);
}
if (timeoutHandle !== undefined) {
clearTimeout(timeoutHandle);
}
};
}, [routes, prefetch]);
}

Expand Down Expand Up @@ -115,31 +132,57 @@ export function useNetworkAwarePrefetch() {
// Prefetch critical routes on app load
export function CriticalRoutePrefetcher() {
useEffect(() => {
let cancelled = false;
let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
let idleHandle: number | undefined;

const wait = (ms: number) => new Promise<void>((resolve) => {
timeoutHandle = setTimeout(() => {
timeoutHandle = undefined;
resolve();
}, ms);
});

const prefetchCritical = async () => {
// Wait for main content to load
await new Promise((resolve) => setTimeout(resolve, 2000));
await wait(2000);
if (cancelled) return;

// Prefetch critical routes
const criticalRoutes = ['dashboard', 'contacts', 'settings'];

for (const route of criticalRoutes) {
if (cancelled) break;
const prefetchFn = routePrefetchConfig[route];
if (prefetchFn) {
try {
await prefetchFn();
if (cancelled) break;
// Small delay between prefetches to not block main thread
await new Promise((resolve) => setTimeout(resolve, 100));
await wait(100);
} catch (err) { log.error('Unexpected error in Prefetcher:', err); }
}
}
};

// Use requestIdleCallback if available
if ('requestIdleCallback' in window) {
window.requestIdleCallback(() => prefetchCritical());
const hasIdleCallback = typeof window !== 'undefined' && 'requestIdleCallback' in window;
if (hasIdleCallback) {
idleHandle = window.requestIdleCallback(() => {
void prefetchCritical();
});
} else {
prefetchCritical();
void prefetchCritical();
}

return () => {
cancelled = true;
if (timeoutHandle !== undefined) {
clearTimeout(timeoutHandle);
}
if (idleHandle !== undefined && hasIdleCallback && 'cancelIdleCallback' in window) {
window.cancelIdleCallback(idleHandle);
}
};
}, []);

return null;
Expand Down
2 changes: 2 additions & 0 deletions src/components/team-chat/MessageReactions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import { cn } from '@/lib/utils';
import { motion } from 'framer-motion';
import type { AggregatedReaction } from '@/features/inbox/hooks/team-chat/useTeamMessageReactions';

// eslint-disable-next-line react-refresh/only-export-components
export const QUICK_EMOJIS = ['👍', '❤️', '😂', '😮', '😢', '🙏'];
// eslint-disable-next-line react-refresh/only-export-components
export const EXTENDED_EMOJIS = [
...QUICK_EMOJIS,
'🔥', '🎉', '👏', '💯', '✅', '❌', '👀', '🤔', '😍', '😎',
Expand Down
2 changes: 2 additions & 0 deletions src/components/team-chat/TeamMemberProfileHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ interface MemberProfile {

export type { MemberProfile };

// eslint-disable-next-line react-refresh/only-export-components
export function getBirthdayInfo(birthday: string | null) {
if (!birthday) return null;
const date = new Date(birthday);
Expand All @@ -35,6 +36,7 @@ export function getBirthdayInfo(birthday: string | null) {
return { date, age, isToday, daysUntil };
}

// eslint-disable-next-line react-refresh/only-export-components
export function getRoleBadge(role: string | null) {
const map: Record<string, { label: string; className: string }> = {
admin: { label: 'Admin', className: 'bg-destructive/10 text-destructive border-destructive/20' },
Expand Down
1 change: 1 addition & 0 deletions src/components/theme/HighContrastToggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export function HighContrastProvider({ children }: { children: React.ReactNode }
);
}

// eslint-disable-next-line react-refresh/only-export-components
export function useHighContrast() {
const context = useContext(HighContrastContext);
if (!context) {
Expand Down
1 change: 1 addition & 0 deletions src/components/ui/accessible-toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface ToastContextValue {

const ToastContext = createContext<ToastContextValue | null>(null);

// eslint-disable-next-line react-refresh/only-export-components
export function useAccessibleToast() {
const context = useContext(ToastContext);
if (!context) {
Expand Down
1 change: 1 addition & 0 deletions src/components/ui/avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ export {
AvatarWithStatus,
AvatarGroup,
StatusIndicator,
// eslint-disable-next-line react-refresh/only-export-components
avatarVariants,
type StatusType,
};
1 change: 1 addition & 0 deletions src/components/ui/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(({ className, variant

Badge.displayName = "Badge";

// eslint-disable-next-line react-refresh/only-export-components
export { Badge, badgeVariants };
1 change: 1 addition & 0 deletions src/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,5 @@ const MotionButton = React.forwardRef<HTMLButtonElement, MotionButtonProps>(
);
MotionButton.displayName = "MotionButton";

// eslint-disable-next-line react-refresh/only-export-components
export { Button, MotionButton, buttonVariants };
1 change: 1 addition & 0 deletions src/components/ui/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,5 @@ const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDiv
);
CardFooter.displayName = "CardFooter";

// eslint-disable-next-line react-refresh/only-export-components
export { Card, MotionCardComponent, CardHeader, CardFooter, CardTitle, CardDescription, CardContent, cardVariants };
1 change: 1 addition & 0 deletions src/components/ui/command-palette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ export function CommandPalette({
);
}

// eslint-disable-next-line react-refresh/only-export-components
export function useCommandPalette() {
const [isOpen, setIsOpen] = React.useState(false);
React.useEffect(() => {
Expand Down
1 change: 1 addition & 0 deletions src/components/ui/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,6 @@ export {
DialogFooter,
DialogTitle,
DialogDescription,
// eslint-disable-next-line react-refresh/only-export-components
dialogContentVariants,
};
1 change: 1 addition & 0 deletions src/components/ui/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,5 @@ const FormMessage = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<
);
FormMessage.displayName = "FormMessage";

// eslint-disable-next-line react-refresh/only-export-components
export { useFormField, Form, FormItem, FormLabel, FormControl, FormDescription, FormMessage, FormField };
1 change: 1 addition & 0 deletions src/components/ui/icon-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,5 @@ const MotionIconButton = React.forwardRef<HTMLButtonElement, MotionIconButtonPro
);
MotionIconButton.displayName = 'MotionIconButton';

// eslint-disable-next-line react-refresh/only-export-components
export { IconButton, MotionIconButton, iconButtonVariants };
1 change: 1 addition & 0 deletions src/components/ui/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,5 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
);
Input.displayName = "Input";

// eslint-disable-next-line react-refresh/only-export-components
export { Input, inputVariants };
Loading
Loading