diff --git a/src/components/contacts/ContactBulkTagDialog.tsx b/src/components/contacts/ContactBulkTagDialog.tsx index 12d7b8922..beb3f0bb0 100644 --- a/src/components/contacts/ContactBulkTagDialog.tsx +++ b/src/components/contacts/ContactBulkTagDialog.tsx @@ -40,7 +40,8 @@ export function ContactBulkTagDialog({ const toggleTag = (tag: string) => { setSelectedTags(prev => { const next = new Set(prev); - next.has(tag) ? next.delete(tag) : next.add(tag); + if (next.has(tag)) next.delete(tag); + else next.add(tag); return next; }); }; diff --git a/src/components/contacts/ContactGroupedList.tsx b/src/components/contacts/ContactGroupedList.tsx index 39d4a044f..319015e25 100644 --- a/src/components/contacts/ContactGroupedList.tsx +++ b/src/components/contacts/ContactGroupedList.tsx @@ -40,7 +40,8 @@ export function ContactGroupedList({ const toggleGroup = (key: string) => { setCollapsed(prev => { const next = new Set(prev); - next.has(key) ? next.delete(key) : next.add(key); + if (next.has(key)) next.delete(key); + else next.add(key); return next; }); }; diff --git a/src/components/contacts/SafeHtml.tsx b/src/components/contacts/SafeHtml.tsx index 4f6f37f66..40728b0d1 100644 --- a/src/components/contacts/SafeHtml.tsx +++ b/src/components/contacts/SafeHtml.tsx @@ -47,7 +47,7 @@ export const SafeHtml: React.FC = ({ } const Tag = inline ? 'span' : 'div'; - // eslint-disable-next-line react/no-danger + // dangerouslySetInnerHTML é seguro aqui: input passou por DOMPurify acima return ; }; diff --git a/src/components/dashboard/ProgressiveDisclosureDashboard.tsx b/src/components/dashboard/ProgressiveDisclosureDashboard.tsx index c03cd7fdb..cbeb42936 100644 --- a/src/components/dashboard/ProgressiveDisclosureDashboard.tsx +++ b/src/components/dashboard/ProgressiveDisclosureDashboard.tsx @@ -43,7 +43,8 @@ export function EnhancedProgressiveDisclosure({ sections, renderWidget, onRefres const toggleSection = (id: string) => { setOpenSections(prev => { const next = new Set(prev); - next.has(id) ? next.delete(id) : next.add(id); + if (next.has(id)) next.delete(id); + else next.add(id); return next; }); }; diff --git a/src/components/settings/media-library/useMediaLibrary.ts b/src/components/settings/media-library/useMediaLibrary.ts index 94fd4e607..1528321f4 100644 --- a/src/components/settings/media-library/useMediaLibrary.ts +++ b/src/components/settings/media-library/useMediaLibrary.ts @@ -125,8 +125,8 @@ export function useMediaLibrary(type: MediaType) { return matchSearch && matchCategory; }); - const toggleSelect = (id: string) => { setSelected(prev => { const next = new Set(prev); next.has(id) ? next.delete(id) : next.add(id); return next; }); }; - const toggleSelectAll = () => { selected.size === filtered.length ? setSelected(new Set()) : setSelected(new Set(filtered.map(i => i.id))); }; + const toggleSelect = (id: string) => { setSelected(prev => { const next = new Set(prev); if (next.has(id)) next.delete(id); else next.add(id); return next; }); }; + const toggleSelectAll = () => { if (selected.size === filtered.length) setSelected(new Set()); else setSelected(new Set(filtered.map(i => i.id))); }; const handleToggleFavorite = async (item: MediaItem) => { const newValue = !item.is_favorite; @@ -183,7 +183,8 @@ export function useMediaLibrary(type: MediaType) { setReclassifying(false); setSelected(new Set()); const msg = `${updated}/${toReclassify.length} itens reclassificados com IA`; - errors > 0 ? toast.info(`${msg} (${errors} erros)`) : toast.success(msg); + if (errors > 0) toast.info(`${msg} (${errors} erros)`); + else toast.success(msg); }; const handleSingleCategoryChange = async (item: MediaItem, newCategory: string) => { diff --git a/src/components/transcriptions/TranscriptionsHistoryView.tsx b/src/components/transcriptions/TranscriptionsHistoryView.tsx index 5019328dc..c47579c17 100644 --- a/src/components/transcriptions/TranscriptionsHistoryView.tsx +++ b/src/components/transcriptions/TranscriptionsHistoryView.tsx @@ -75,7 +75,7 @@ export function TranscriptionsHistoryView() { return grouped; }, [filteredTranscriptions]); - const toggleContact = (id: string) => setExpandedContacts(prev => { const next = new Set(prev); next.has(id) ? next.delete(id) : next.add(id); return next; }); + const toggleContact = (id: string) => setExpandedContacts(prev => { const next = new Set(prev); if (next.has(id)) next.delete(id); else next.add(id); return next; }); const expandAll = () => setExpandedContacts(new Set(Object.keys(groupedByContact))); const collapseAll = () => setExpandedContacts(new Set()); diff --git a/src/components/ui/command.tsx b/src/components/ui/command.tsx index 950f6d434..890c7bb1d 100644 --- a/src/components/ui/command.tsx +++ b/src/components/ui/command.tsx @@ -21,7 +21,7 @@ const Command = React.forwardRef< )); Command.displayName = CommandPrimitive.displayName; -interface CommandDialogProps extends DialogProps {} +type CommandDialogProps = DialogProps; const CommandDialog = ({ children, ...props }: CommandDialogProps) => { return ( diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx index 3d0c8c8d8..23390bb03 100644 --- a/src/components/ui/textarea.tsx +++ b/src/components/ui/textarea.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import { cn } from "@/lib/utils"; -export interface TextareaProps extends React.TextareaHTMLAttributes {} +export type TextareaProps = React.TextareaHTMLAttributes; const Textarea = React.forwardRef(({ className, ...props }, ref) => { return ( diff --git a/src/features/inbox/components/AudioRecorder.tsx b/src/features/inbox/components/AudioRecorder.tsx index 0b6cea319..e0c1dd531 100644 --- a/src/features/inbox/components/AudioRecorder.tsx +++ b/src/features/inbox/components/AudioRecorder.tsx @@ -94,7 +94,8 @@ export function AudioRecorder({ onSend, onCancel }: AudioRecorderProps) { if (isRecording || isPaused) { if (e.key === ' ' || e.key === 'p' || e.key === 'P') { e.preventDefault(); - isPaused ? resumeRecording() : pauseRecording(); + if (isPaused) resumeRecording(); + else pauseRecording(); } if (e.key === 'Escape') { e.preventDefault(); diff --git a/src/features/inbox/components/useFileUploadLogic.ts b/src/features/inbox/components/useFileUploadLogic.ts index 0585ccbdc..bc17001ad 100644 --- a/src/features/inbox/components/useFileUploadLogic.ts +++ b/src/features/inbox/components/useFileUploadLogic.ts @@ -216,7 +216,8 @@ export function useFileUploadLogic(opts: { setCurrentQueueIndex(i); if (fileQueue[i].validation.valid) { const success = await sendSingleQueueFile(fileQueue[i], i); - success ? successCount++ : errorCount++; + if (success) successCount++; + else errorCount++; if (i < fileQueue.length - 1) await new Promise(r => setTimeout(r, 500)); } else { errorCount++; } } diff --git a/src/features/inbox/hooks/useRealtimeInbox.ts b/src/features/inbox/hooks/useRealtimeInbox.ts index 61e1f8ed2..0c19b45ab 100644 --- a/src/features/inbox/hooks/useRealtimeInbox.ts +++ b/src/features/inbox/hooks/useRealtimeInbox.ts @@ -419,8 +419,6 @@ export function useRealtimeInbox() { } else { await sendMessage(contactId, content); } - } catch (err) { - throw err; } finally { await refreshActiveConversation(); } diff --git a/src/features/inbox/hooks/useSipClient.ts b/src/features/inbox/hooks/useSipClient.ts index b1e614785..d31596254 100644 --- a/src/features/inbox/hooks/useSipClient.ts +++ b/src/features/inbox/hooks/useSipClient.ts @@ -107,7 +107,10 @@ export function useSipClient() { const hangUp = useCallback(() => { if (sessionRef.current) { - try { sessionRef.current.state === SessionState.Established ? sessionRef.current.bye() : sessionRef.current.cancel(); } catch (err) { log.error('Hangup error:', err); } + try { + if (sessionRef.current.state === SessionState.Established) sessionRef.current.bye(); + else sessionRef.current.cancel(); + } catch (err) { log.error('Hangup error:', err); } sessionRef.current = null; } stopTimer(); setCallStatus('idle'); callStatusRef.current = 'idle'; setIsMuted(false); diff --git a/src/hooks/useAutomations.ts b/src/hooks/useAutomations.ts index 1ddf4b403..6f5be7c31 100644 --- a/src/hooks/useAutomations.ts +++ b/src/hooks/useAutomations.ts @@ -77,7 +77,9 @@ export function useAutomations({ if (!rules.length) return; // Pega últimas 10 msgs do FATOR X - const { data: msgs } = await getClient()?.rpc("rpc_list_messages", { + const client = getClient(); + if (!client) return; + const { data: msgs } = await client.rpc("rpc_list_messages", { p_remote_jid: remoteJid, p_instance: instanceName, p_limit: 10, @@ -100,7 +102,8 @@ export function useAutomations({ let addedTags: string[] = []; let removedTags: string[] = []; try { - const { data: contact } = await getClient()?.rpc("rpc_get_contact", { + // Reusa client validado acima — falha aqui só pula snapshot de tags, não aborta evaluate + const { data: contact } = await client.rpc("rpc_get_contact", { p_remote_jid: remoteJid, p_instance: instanceName, } as any); diff --git a/src/hooks/usePerformanceOptimizations.ts b/src/hooks/usePerformanceOptimizations.ts index 7a18f863f..709b60dc6 100644 --- a/src/hooks/usePerformanceOptimizations.ts +++ b/src/hooks/usePerformanceOptimizations.ts @@ -208,7 +208,7 @@ export function useNetworkStatus() { window.addEventListener('offline', handleOffline); // Connection API - const connection = (navigator as Navigator & { connection?: { effectiveType?: string; saveData?: boolean; addEventListener?: Function; removeEventListener?: Function } }).connection; + const connection = (navigator as Navigator & { connection?: { effectiveType?: string; saveData?: boolean; addEventListener?: (type: string, listener: () => void) => void; removeEventListener?: (type: string, listener: () => void) => void } }).connection; if (connection) { setConnectionType(connection.effectiveType); setIsSlowConnection( @@ -226,12 +226,17 @@ export function useNetworkStatus() { ); }; - connection.addEventListener('change', handleChange); + // NetworkInformation API não é suportada em Firefox/Safari — guard + if (typeof connection.addEventListener === 'function') { + connection.addEventListener('change', handleChange); + } return () => { window.removeEventListener('online', handleOnline); window.removeEventListener('offline', handleOffline); - connection.removeEventListener('change', handleChange); + if (typeof connection.removeEventListener === 'function') { + connection.removeEventListener('change', handleChange); + } }; } diff --git a/src/lib/logger.ts b/src/lib/logger.ts index b4ab66a43..a8ec37f36 100644 --- a/src/lib/logger.ts +++ b/src/lib/logger.ts @@ -81,12 +81,11 @@ class Logger { /** Log with explicit correlation ID for request tracing */ withCorrelation(correlationId: string) { - const self = this; return { - debug: (msg: string, ...a: unknown[]) => self.debug(`[cid:${correlationId}] ${msg}`, ...a), - info: (msg: string, ...a: unknown[]) => self.info(`[cid:${correlationId}] ${msg}`, ...a), - warn: (msg: string, ...a: unknown[]) => self.warn(`[cid:${correlationId}] ${msg}`, ...a), - error: (msg: string, ...a: unknown[]) => self.error(`[cid:${correlationId}] ${msg}`, ...a), + debug: (msg: string, ...a: unknown[]) => this.debug(`[cid:${correlationId}] ${msg}`, ...a), + info: (msg: string, ...a: unknown[]) => this.info(`[cid:${correlationId}] ${msg}`, ...a), + warn: (msg: string, ...a: unknown[]) => this.warn(`[cid:${correlationId}] ${msg}`, ...a), + error: (msg: string, ...a: unknown[]) => this.error(`[cid:${correlationId}] ${msg}`, ...a), }; } } diff --git a/tailwind.config.ts b/tailwind.config.ts index cb259f7dd..f0ad5e630 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,4 +1,5 @@ import type { Config } from "tailwindcss"; +import tailwindcssAnimate from "tailwindcss-animate"; export default { darkMode: ["class"], @@ -317,5 +318,5 @@ export default { }, }, }, - plugins: [require("tailwindcss-animate")], + plugins: [tailwindcssAnimate], } satisfies Config;