chore(eslint): fix 20 residual errors (Onda 5 PR 5.2.1)#111
Conversation
Reduces ESLint errors from 128 → 108 (-20, -16%) by addressing all
non-import errors. Remaining 108 errors are all no-restricted-imports
which will be tackled in PR 5.3 (DDD refactor).
## Batch A — Mechanical (18 fixes)
### A.1 — no-unused-expressions (10 fixes, all ternary-as-statement)
Pattern: `cond ? a() : b();` → `if (cond) a(); else b();`
Files (all with same fix pattern):
- ContactBulkTagDialog.tsx, ContactGroupedList.tsx
- ProgressiveDisclosureDashboard.tsx
- useMediaLibrary.ts (3 occurrences)
- TranscriptionsHistoryView.tsx
- AudioRecorder.tsx, useFileUploadLogic.ts, useSipClient.ts
Semantically identical: ESLint flags ternary-as-statement because
the result is discarded; if/else is the idiomatic alternative.
### A.2 — no-empty-object-type (2 fixes)
- src/components/ui/command.tsx:24 — `interface CommandDialogProps extends DialogProps {}` → `type CommandDialogProps = DialogProps`
- src/components/ui/textarea.tsx:5 — same pattern
Both are internal types with no augmentation use case, so type alias
is equivalent and cleaner.
### A.3 — no-useless-catch (1 fix)
- src/features/inbox/hooks/useRealtimeInbox.ts:422
Removed `try { ... } catch (err) { throw err } finally { ... }` →
`try { ... } finally { ... }`. Catch-then-rethrow is a no-op.
### A.4 — no-this-alias (1 fix)
- src/lib/logger.ts:84 — `const self = this` removed.
Arrow functions in the returned object literal already capture
`this` from the parent method scope.
### A.5 — no-require-imports (1 fix)
- tailwind.config.ts:320 — `require('tailwindcss-animate')` →
`import tailwindcssAnimate from 'tailwindcss-animate'` at top.
Tailwind config is ESM, so this is the correct form.
### A.6 — no-unsafe-function-type (2 fixes, same line)
- src/hooks/usePerformanceOptimizations.ts:211 —
`addEventListener?: Function; removeEventListener?: Function` →
`addEventListener?: (type: string, listener: () => void) => void; removeEventListener?: (type: string, listener: () => void) => void`
The Network Information API uses standard EventTarget contract.
### A.7 — react/no-danger 'rule not found' (1 fix)
- src/components/contacts/SafeHtml.tsx:50 — removed
`// eslint-disable-next-line react/no-danger` because
eslint-plugin-react is NOT installed in the project (only
react-hooks and react-refresh). The disable was referencing
a rule that never existed in the config — ESLint 9 flags this.
Replaced with a code comment documenting that
dangerouslySetInnerHTML is safe here (input passed through
DOMPurify.sanitize() above).
## Batch B — Logic (2 fixes)
### B.1 — no-unsafe-optional-chaining (2 fixes in useAutomations.ts)
- :80 — `await getClient()?.rpc(...)` could destructure undefined
if getClient() returns null. Refactored:
`const client = getClient(); if (!client) return; const { data } = await client.rpc(...)`
- :103 — same pattern, same refactor
## Validation
- bun run lint → 1438 problems (108 errors, 1330 warnings)
- Errors went from 128 → 108 (-20)
- All remaining errors are no-restricted-imports (PR 5.3 territory)
- bun run build → ✓ built in 59.58s (no regressions)
## Stress-test results
- Ternary → if/else: semantically identical (✓)
- Empty interface → type alias: both internal, no augmentation (✓)
- try/catch/finally → try/finally: re-throw is no-op (✓)
- const self = this → this in arrow: arrows inherit lexical this (✓)
- require → import: Tailwind ESM-compatible (✓)
- Function → specific signature: matches Network Info API (✓)
- react/no-danger removal: plugin not installed, no behavior change (✓)
- getClient() guard: prevents .rpc() on null (✓)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughPR aplica refatorações locais: ternary → if/else em toggles/handlers, converte interfaces vazias para type aliases, valida cliente Supabase com getClient(), refina listeners, remove captura self no Logger e migra plugin Tailwind para import ES. ChangesDefinições de Tipo e Refinamento
Padronização de Controle de Fluxo
Resolução de Cliente e Error Handling
Configuração e Utilities
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/hooks/usePerformanceOptimizations.ts (1)
211-235:⚠️ Potential issue | 🟠 Major | ⚡ Quick winProteja registro de listener em
navigator.connectione considere fallback
connectioné checado, masaddEventListener/removeEventListenersão opcionais na tipagem (linha 211) e são chamados sem guarda (linhas 229 e 234). O NetworkInformation API não é suportado em Firefox nem Safari — se a API não expõe esses métodos ou não existe naquela plataforma, o effect quebra e deixa listeners dewindowsem cleanup.Patch sugerido
if (connection) { setConnectionType(connection.effectiveType); setIsSlowConnection( connection.saveData || connection.effectiveType === 'slow-2g' || connection.effectiveType === '2g' ); const handleChange = () => { setConnectionType(connection.effectiveType); setIsSlowConnection( connection.saveData || connection.effectiveType === 'slow-2g' || connection.effectiveType === '2g' ); }; - connection.addEventListener('change', handleChange); + if (connection.addEventListener && connection.removeEventListener) { + connection.addEventListener('change', handleChange); + } else if ('onchange' in connection) { + connection.onchange = handleChange; + } return () => { window.removeEventListener('online', handleOnline); window.removeEventListener('offline', handleOffline); - connection.removeEventListener('change', handleChange); + if (connection.removeEventListener) { + connection.removeEventListener('change', handleChange); + } else if ('onchange' in connection) { + connection.onchange = null; + } }; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/usePerformanceOptimizations.ts` around lines 211 - 235, The effect currently calls connection.addEventListener/removeEventListener without guarding that those methods exist; update the connection branch to check that connection.addEventListener and connection.removeEventListener are functions before calling them (e.g., only add the handleChange listener when typeof connection.addEventListener === 'function'), and likewise only call connection.removeEventListener in the cleanup when typeof connection.removeEventListener === 'function'; keep using the same handleChange, setConnectionType and setIsSlowConnection logic and still clean up window listeners (handleOnline/handleOffline) regardless.
🧹 Nitpick comments (1)
src/features/inbox/hooks/useSipClient.ts (1)
110-113: ⚡ Quick winRestrinja
cancel()apenas para estados válidos no sip.jsO
elsechamacancel()para qualquer estado diferente deEstablished, inclusiveTerminated— estado terminal onde não faz sentido tentar cancelar de novo. Isso gera exceções esperadas e polui o log. Restrinja acancel()apenas nos estados onde é válido (Initial e Establishing).Patch sugerido
try { - if (sessionRef.current.state === SessionState.Established) sessionRef.current.bye(); - else sessionRef.current.cancel(); + const state = sessionRef.current.state; + if (state === SessionState.Established) { + sessionRef.current.bye(); + } else if (state === SessionState.Initial || state === SessionState.Establishing) { + sessionRef.current.cancel(); + } } catch (err) { log.error('Hangup error:', err); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/inbox/hooks/useSipClient.ts` around lines 110 - 113, The hangup logic calls sessionRef.current.cancel() for any non-Established state (including Terminal), causing noisy exceptions; change the conditional so you call sessionRef.current.bye() when sessionRef.current.state === SessionState.Established, and only call sessionRef.current.cancel() when sessionRef.current.state is one of the valid cancelable states (e.g., SessionState.Initial or SessionState.Establishing); keep the try/catch around the calls and continue logging errors via log.error('Hangup error:', err) but avoid invoking cancel() for Terminated/Terminal states.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/hooks/useAutomations.ts`:
- Around line 105-107: A chamada redundante a getClient() criando client2 e o
return podem abortar evaluate() prematuramente; substitua o uso de client2 por
reutilizar o cliente já validado (client) disponível na função evaluate(),
remova o return que interrompe a execução e, ao chamar
client.rpc("rpc_get_contact", ...), trate o caso de contact ausente de forma
não-bloqueante para que gatilhos sem tags ainda sejam avaliados; localize o
código onde client2 é definido/substituir e atualizar a chamada RPC para usar
client e checar/ignorar contact nulo em vez de abortar.
---
Outside diff comments:
In `@src/hooks/usePerformanceOptimizations.ts`:
- Around line 211-235: The effect currently calls
connection.addEventListener/removeEventListener without guarding that those
methods exist; update the connection branch to check that
connection.addEventListener and connection.removeEventListener are functions
before calling them (e.g., only add the handleChange listener when typeof
connection.addEventListener === 'function'), and likewise only call
connection.removeEventListener in the cleanup when typeof
connection.removeEventListener === 'function'; keep using the same handleChange,
setConnectionType and setIsSlowConnection logic and still clean up window
listeners (handleOnline/handleOffline) regardless.
---
Nitpick comments:
In `@src/features/inbox/hooks/useSipClient.ts`:
- Around line 110-113: The hangup logic calls sessionRef.current.cancel() for
any non-Established state (including Terminal), causing noisy exceptions; change
the conditional so you call sessionRef.current.bye() when
sessionRef.current.state === SessionState.Established, and only call
sessionRef.current.cancel() when sessionRef.current.state is one of the valid
cancelable states (e.g., SessionState.Initial or SessionState.Establishing);
keep the try/catch around the calls and continue logging errors via
log.error('Hangup error:', err) but avoid invoking cancel() for
Terminated/Terminal states.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: cbd13a58-e08a-464b-8590-eaeebe3263c5
📒 Files selected for processing (16)
src/components/contacts/ContactBulkTagDialog.tsxsrc/components/contacts/ContactGroupedList.tsxsrc/components/contacts/SafeHtml.tsxsrc/components/dashboard/ProgressiveDisclosureDashboard.tsxsrc/components/settings/media-library/useMediaLibrary.tssrc/components/transcriptions/TranscriptionsHistoryView.tsxsrc/components/ui/command.tsxsrc/components/ui/textarea.tsxsrc/features/inbox/components/AudioRecorder.tsxsrc/features/inbox/components/useFileUploadLogic.tssrc/features/inbox/hooks/useRealtimeInbox.tssrc/features/inbox/hooks/useSipClient.tssrc/hooks/useAutomations.tssrc/hooks/usePerformanceOptimizations.tssrc/lib/logger.tstailwind.config.ts
💤 Files with no reviewable changes (1)
- src/features/inbox/hooks/useRealtimeInbox.ts
There was a problem hiding this comment.
Pull request overview
This PR reduces ESLint error count by applying a set of mechanical refactors (ternary-as-statement → if/else, removing no-op catch, removing this aliasing, replacing empty interface extensions with type aliases) plus a couple of small logic guards around optional Supabase client usage.
Changes:
- Replaced statement-style ternaries with explicit
if/elseblocks across UI and hook codepaths. - Removed a useless
catch { throw err }while preservingfinallybehavior, and removedconst self = thisby relying on arrow-functionthisbinding. - Replaced
require()and unsafeFunctiontypings with ES imports and explicit function signatures.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tailwind.config.ts | Replaces require("tailwindcss-animate") with an ES import and uses it in plugins. |
| src/lib/logger.ts | Removes self = this alias in withCorrelation() and uses arrow functions capturing this. |
| src/hooks/usePerformanceOptimizations.ts | Replaces Function-typed Connection API listeners with explicit function signatures. |
| src/hooks/useAutomations.ts | Adds guards for potentially-null external Supabase client before RPC calls. |
| src/features/inbox/hooks/useSipClient.ts | Expands hangup ternary statement into a readable if/else. |
| src/features/inbox/hooks/useRealtimeInbox.ts | Removes redundant catch/rethrow while keeping finally refresh behavior. |
| src/features/inbox/components/useFileUploadLogic.ts | Replaces ternary-as-statement counter updates with if/else. |
| src/features/inbox/components/AudioRecorder.tsx | Replaces pause/resume ternary-as-statement with if/else in key handler. |
| src/components/ui/textarea.tsx | Converts empty interface extension to a type alias. |
| src/components/ui/command.tsx | Converts empty interface extension to a type alias. |
| src/components/transcriptions/TranscriptionsHistoryView.tsx | Replaces set-toggle ternary-as-statement with explicit if/else. |
| src/components/settings/media-library/useMediaLibrary.ts | Replaces ternary-as-statement in selection + toast logic with if/else. |
| src/components/dashboard/ProgressiveDisclosureDashboard.tsx | Replaces set-toggle ternary-as-statement with explicit if/else. |
| src/components/contacts/SafeHtml.tsx | Removes disable for missing react/no-danger rule; keeps explanatory comment. |
| src/components/contacts/ContactGroupedList.tsx | Replaces set-toggle ternary-as-statement with explicit if/else. |
| src/components/contacts/ContactBulkTagDialog.tsx | Replaces set-toggle ternary-as-statement with explicit if/else. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const client2 = getClient(); | ||
| if (!client2) return; | ||
| const { data: contact } = await client2.rpc("rpc_get_contact", { |
| 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) { |
CodeRabbit + Copilot review feedback addressed: ## Issue 1+2 (CodeRabbit + Copilot, useAutomations.ts:107) **Before:** ```ts const client2 = getClient(); if (!client2) return; //⚠️ Aborta evaluate inteiro! const { data: contact } = await client2.rpc(...) ``` **Problem:** `getClient()` é chamado 2x desnecessariamente, e o `return` dentro do try aborta evaluate() inteiro, pulando todos os outros gatilhos que não dependem da snapshot de tags. **Fix:** Reusa o `client` já validado na linha 80 (15 linhas antes). Se `client.rpc()` falhar, o catch existente trata e segue evaluate. ```ts try { // Reusa client validado acima — falha aqui só pula snapshot de tags, não aborta evaluate const { data: contact } = await client.rpc(...) ``` ## Issue 3 (CodeRabbit outside-diff, usePerformanceOptimizations.ts:211-235) **Before:** ```ts connection.addEventListener('change', handleChange); // cleanup: connection.removeEventListener('change', handleChange); ``` **Problem:** A NetworkInformation API NÃO é suportada em Firefox e Safari. Em browsers sem suporte, `connection` pode existir mas não ter `addEventListener/removeEventListener` — chamadas quebram o useEffect e deixam listeners de window sem cleanup. **Fix:** Guard com `typeof === 'function'` antes de chamar. ```ts if (typeof connection.addEventListener === 'function') { connection.addEventListener('change', handleChange); } // cleanup: if (typeof connection.removeEventListener === 'function') { connection.removeEventListener('change', handleChange); } ``` ## Validation - bun run lint → 108 errors (unchanged, all no-restricted-imports) - bun run build → ✓ built in 58.18s
|
@coderabbitai full review Address review feedback aplicado em commit ✅ Issue 1+2 (useAutomations.ts:107) — reusando |
|
✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/hooks/usePerformanceOptimizations.ts (1)
211-218:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdicione fallbacks para propriedades opcionais da Network Information API
Em linhas 213 e 220-226,
connection.effectiveTypeeconnection.saveDatapodem virundefinedem navegadores sem suporte completo à API. Isso propaga estado inconsistente —connectionTypeé inicializado comostringmas pode receberundefined.Diff sugerido
if (connection) { + const effectiveType = connection.effectiveType ?? 'unknown'; + const saveData = connection.saveData ?? false; - setConnectionType(connection.effectiveType); + setConnectionType(effectiveType); setIsSlowConnection( - connection.saveData || - connection.effectiveType === 'slow-2g' || - connection.effectiveType === '2g' + saveData || + effectiveType === 'slow-2g' || + effectiveType === '2g' ); const handleChange = () => { + const nextEffectiveType = connection.effectiveType ?? 'unknown'; + const nextSaveData = connection.saveData ?? false; - setConnectionType(connection.effectiveType); + setConnectionType(nextEffectiveType); setIsSlowConnection( - connection.saveData || - connection.effectiveType === 'slow-2g' || - connection.effectiveType === '2g' + nextSaveData || + nextEffectiveType === 'slow-2g' || + nextEffectiveType === '2g' ); };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/usePerformanceOptimizations.ts` around lines 211 - 218, The hook usePerformanceOptimizations reads navigator.connection which may have undefined properties; update the code that calls setConnectionType and setIsSlowConnection to guard and provide fallbacks: compute a safe string like const effectiveType = connection?.effectiveType ?? 'unknown' before calling setConnectionType(effectiveType), and compute a boolean for slow connection using Boolean(connection?.saveData) || effectiveType === 'slow-2g' || effectiveType === '2g' before calling setIsSlowConnection; ensure you still attach/remove listeners on connection when available and keep the same identifiers (usePerformanceOptimizations, setConnectionType, setIsSlowConnection, connection.effectiveType, connection.saveData).
🧹 Nitpick comments (1)
src/components/settings/media-library/useMediaLibrary.ts (1)
128-129: ⚡ Quick winComparação por tamanho em
toggleSelectAllpode limpar seleção erradaNa Line 129,
selected.size === filtered.lengthnão garante que os mesmos IDs estão selecionados; com tamanhos iguais e conjuntos diferentes, o “selecionar todos” pode limpar indevidamente.Diff sugerido
- const toggleSelectAll = () => { if (selected.size === filtered.length) setSelected(new Set()); else setSelected(new Set(filtered.map(i => i.id))); }; + const toggleSelectAll = () => { + const allFilteredSelected = filtered.length > 0 && filtered.every(i => selected.has(i.id)); + if (allFilteredSelected) setSelected(new Set()); + else setSelected(new Set(filtered.map(i => i.id))); + };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/settings/media-library/useMediaLibrary.ts` around lines 128 - 129, The toggleSelectAll logic compares selected.size to filtered.length which can be equal while containing different IDs; update the toggleSelectAll in useMediaLibrary.ts to check whether all filtered items are actually selected (e.g., verify every filtered id is present in the selected Set) and only clear the selection if that containment check is true, otherwise set selected to a new Set of filtered.map(i => i.id); reference the toggleSelectAll function and the selected/filtered variables when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/hooks/usePerformanceOptimizations.ts`:
- Around line 211-218: The hook usePerformanceOptimizations reads
navigator.connection which may have undefined properties; update the code that
calls setConnectionType and setIsSlowConnection to guard and provide fallbacks:
compute a safe string like const effectiveType = connection?.effectiveType ??
'unknown' before calling setConnectionType(effectiveType), and compute a boolean
for slow connection using Boolean(connection?.saveData) || effectiveType ===
'slow-2g' || effectiveType === '2g' before calling setIsSlowConnection; ensure
you still attach/remove listeners on connection when available and keep the same
identifiers (usePerformanceOptimizations, setConnectionType,
setIsSlowConnection, connection.effectiveType, connection.saveData).
---
Nitpick comments:
In `@src/components/settings/media-library/useMediaLibrary.ts`:
- Around line 128-129: The toggleSelectAll logic compares selected.size to
filtered.length which can be equal while containing different IDs; update the
toggleSelectAll in useMediaLibrary.ts to check whether all filtered items are
actually selected (e.g., verify every filtered id is present in the selected
Set) and only clear the selection if that containment check is true, otherwise
set selected to a new Set of filtered.map(i => i.id); reference the
toggleSelectAll function and the selected/filtered variables when making the
change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 419fe45d-e76a-41de-8476-89fd9cb29dcb
📒 Files selected for processing (16)
src/components/contacts/ContactBulkTagDialog.tsxsrc/components/contacts/ContactGroupedList.tsxsrc/components/contacts/SafeHtml.tsxsrc/components/dashboard/ProgressiveDisclosureDashboard.tsxsrc/components/settings/media-library/useMediaLibrary.tssrc/components/transcriptions/TranscriptionsHistoryView.tsxsrc/components/ui/command.tsxsrc/components/ui/textarea.tsxsrc/features/inbox/components/AudioRecorder.tsxsrc/features/inbox/components/useFileUploadLogic.tssrc/features/inbox/hooks/useRealtimeInbox.tssrc/features/inbox/hooks/useSipClient.tssrc/hooks/useAutomations.tssrc/hooks/usePerformanceOptimizations.tssrc/lib/logger.tstailwind.config.ts
💤 Files with no reviewable changes (1)
- src/features/inbox/hooks/useRealtimeInbox.ts
🎉 Fecha Onda 5: 108 → 0 errors (-108). ESLint TOTAL: 164 → 0 errors em 4 PRs (#109, #110, #111, este). ## Estratégia DDD Regra `no-restricted-imports` (eslint.config.js) bloqueia: 1. Imports cross-feature deep (`@/features/X/...`) — força entry points 2. Imports relativos profundos (`../../...`) 3. INTRA-feature via alias (`@/features/X/Y` quando arquivo está em X) ## Batch A — INTRA-feature (106 fixes em 42 arquivos) Pattern: `@/features/X/Y/Z` → caminho relativo via path.relative() Ex (src/features/inbox/components/chat/ChatHeader.tsx): - ANTES: `@/features/inbox/components/ai-tools/VisionIcon` - DEPOIS: `../ai-tools/VisionIcon` Distribuição: - 92 inbox (52 chat, 12 contact-details, 8 components, 5 hooks, 3 services, 3 virtualized, 3 conversation-list, 2 realtime, 2 templates, 2 search, 1 monitoring, 1 mocks) - 7 auth (5 components, 1 hooks, 1 context) - 3 admin (2 hooks, 1 services) - 3 connections (2 hooks, 1 services) - 1 sla (1 components) Script: /tmp/fix-intra.mjs (Node + path.relative) - 106/106 sucesso, zero falhas - 1 read/write por arquivo (eficiente) - Ordem desc por linha (não invalida índices) ## Batch B — CROSS-feature (2 fixes) Imports de outra feature DEVEM usar entry point (`@/features/X`), não path interno (`@/features/X/hooks/...`). Os barrels já existiam (`auth/index.ts` e `sla/index.ts` fazem `export * from './hooks'`), só não estavam sendo usados: - src/features/inbox/components/ChatPanel.tsx: `@/features/auth/hooks/useUserRole` → `@/features/auth` - src/features/inbox/components/RealtimeInboxView.tsx: `@/features/sla/hooks/useSLAAlerts` → `@/features/sla` ## Validação - bunx eslint . → 0 errors, 1330 warnings (Onda 5 ZERADA) - bunx tsc --noEmit → ✓ sem erros (typecheck pristine) - bun run build → ✓ built in 58.09s (zero regressões) - 44 files changed, 108 insertions(+), 108 deletions(-) ## Stress-test (10 cenários) ✓ path.relative gera `../foo` ou `./foo` corretamente ✓ Sem imports circulares (auth/sla não importam inbox) ✓ Comments inline preservados (replace só no string interno) ✓ BOM/CRLF tratados pelo Node ✓ index.ts implícito resolvido pelo TS path mapping ✓ Build não quebrou — tsconfig path mapping continua funcionando ✓ Typecheck sem regressão ✓ Sem sessão paralela (verificado via git reflog) ✓ Barrels já existiam, useUserRole/useSLAAlerts já re-exportados ✓ Eslint --fix NÃO usado (só explicit replace, sem side effects) ## Pós-Onda 5 Próximo do roadmap pré-deploy: - PR #108 (preserve/faxina) — decidir mergear ou fechar - 8 dependabots — aplicar recomendação A (fechar com TODO pós-deploy) - Fase B10 — patch 9 vulns npm - Fase B5 — ENV_SETUP doc - Fase C — Dockerfile + deploy zapp.atomicabr.com.br
🎉 Fecha Onda 5: 108 → 0 errors (-108). ESLint TOTAL: 164 → 0 errors em 4 PRs (#109, #110, #111, este). ## Estratégia DDD Regra `no-restricted-imports` (eslint.config.js) bloqueia: 1. Imports cross-feature deep (`@/features/X/...`) — força entry points 2. Imports relativos profundos (`../../...`) 3. INTRA-feature via alias (`@/features/X/Y` quando arquivo está em X) ## Batch A — INTRA-feature (106 fixes em 42 arquivos) Pattern: `@/features/X/Y/Z` → caminho relativo via path.relative() Ex (src/features/inbox/components/chat/ChatHeader.tsx): - ANTES: `@/features/inbox/components/ai-tools/VisionIcon` - DEPOIS: `../ai-tools/VisionIcon` Distribuição: - 92 inbox (52 chat, 12 contact-details, 8 components, 5 hooks, 3 services, 3 virtualized, 3 conversation-list, 2 realtime, 2 templates, 2 search, 1 monitoring, 1 mocks) - 7 auth (5 components, 1 hooks, 1 context) - 3 admin (2 hooks, 1 services) - 3 connections (2 hooks, 1 services) - 1 sla (1 components) Script: /tmp/fix-intra.mjs (Node + path.relative) - 106/106 sucesso, zero falhas - 1 read/write por arquivo (eficiente) - Ordem desc por linha (não invalida índices) ## Batch B — CROSS-feature (2 fixes) Imports de outra feature DEVEM usar entry point (`@/features/X`), não path interno (`@/features/X/hooks/...`). Os barrels já existiam (`auth/index.ts` e `sla/index.ts` fazem `export * from './hooks'`), só não estavam sendo usados: - src/features/inbox/components/ChatPanel.tsx: `@/features/auth/hooks/useUserRole` → `@/features/auth` - src/features/inbox/components/RealtimeInboxView.tsx: `@/features/sla/hooks/useSLAAlerts` → `@/features/sla` ## Validação - bunx eslint . → 0 errors, 1330 warnings (Onda 5 ZERADA) - bunx tsc --noEmit → ✓ sem erros (typecheck pristine) - bun run build → ✓ built in 58.09s (zero regressões) - 44 files changed, 108 insertions(+), 108 deletions(-) ## Stress-test (10 cenários) ✓ path.relative gera `../foo` ou `./foo` corretamente ✓ Sem imports circulares (auth/sla não importam inbox) ✓ Comments inline preservados (replace só no string interno) ✓ BOM/CRLF tratados pelo Node ✓ index.ts implícito resolvido pelo TS path mapping ✓ Build não quebrou — tsconfig path mapping continua funcionando ✓ Typecheck sem regressão ✓ Sem sessão paralela (verificado via git reflog) ✓ Barrels já existiam, useUserRole/useSLAAlerts já re-exportados ✓ Eslint --fix NÃO usado (só explicit replace, sem side effects) ## Pós-Onda 5 Próximo do roadmap pré-deploy: - PR #108 (preserve/faxina) — decidir mergear ou fechar - 8 dependabots — aplicar recomendação A (fechar com TODO pós-deploy) - Fase B10 — patch 9 vulns npm - Fase B5 — ENV_SETUP doc - Fase C — Dockerfile + deploy zapp.atomicabr.com.br
…09) (#113) Documenta o estado FINAL após: - Onda 5 completa (164 → 0 errors em 4 PRs: #109, #110, #111, #112) - 8 dependabots resolvidos (3 mergeados, 5 fechados, 1 preservation closed) Estado atual: - HEAD main: f1b3eab - ESLint: 0 errors, 1330 warnings - Branches remotas: 2 (main + preserve) - PRs OPEN: 0 Conteúdo do handoff (718 linhas): - TL;DR + estado atual com métricas - Quem é Joaquim e workflow - Histórico cronológico de TODOS os PRs (Schemas, Faxina pré-Onda, Ondas 1, 2, 5) - Faxina de branches (491 → 2) - Resolução técnica dos 8 dependabots - Decisões técnicas-chave (React 18, Vite reject, Sentry accept, etc) - 20 lições aprendidas - Catálogo de notes em /workspace/notes/ - Pendências pré-deploy (Backup Supabase, RabbitMQ 69GB, Drift, Deploy VPS, FX-DEP-02) - Backlogs de review skipped - Como retomar (instruções pra próximo Claude) - Apêndices: comandos úteis, infra VPS, Vercel, workflow Este doc serve como ponto de retomada pra próxima sessão Claude sem perder contexto.
Resumo
Reduz ESLint errors de 128 → 108 (-20, -16%) limpando todos os errors não-import.
Os 108 restantes são todos
no-restricted-importsque vão pro PR 5.3 (DDD refactor).Diff stat
16 files changed, 35 insertions(+), 24 deletions(-)Batches aplicados
Batch A — Mecânico (18 fixes)
no-unused-expressions(ternário-as-statement)cond ? a() : b()→if (cond) a(); else b();no-empty-object-typeinterface X extends Y {}→type X = Yno-useless-catchcatch { throw err }mantendofinallyno-this-aliasconst self = thisremovido (arrow functions herdamthis)no-require-importsrequire('tailwindcss-animate')→importno topono-unsafe-function-typeFunctiontype →(type, listener) => voidreact/no-danger 'rule not found'Batch B — Lógica (2 fixes)
no-unsafe-optional-chaininggetClient()?.rpc()→ guard comif (!client) returnValidação
bun run lint→ 1438 problems (108 errors, 1330 warnings)bun run build→ ✓ built in 59.58s (zero regressões)Por que A.7?
eslint-plugin-reactnão está instalado neste projeto (sóreact-hooksereact-refresh). O// eslint-disable-next-line react/no-dangerreferenciava uma regra que nunca esteve ativa. ESLint 9 ficou mais rigoroso e flaga isso. OdangerouslySetInnerHTMLé seguro (input passa por DOMPurify), agora documentado em comentário regular.Próximo (PR 5.3)
Os 108
no-restricted-importsrestantes serão resolvidos no PR 5.3 — DDD refactor:Governança
Considerado GRANDE (16 files), seguindo padrão Onda 1+2.
Stress-test de 8 cenários documentado no commit message.
🤖 Generated with Claude
Summary by CodeRabbit
Notas de Lançamento
Refactor
Chores