Skip to content

Commit 3753761

Browse files
authored
fix: Single Source of Truth - correções completas de cache e realtime (#10)
Correções principais: - CRMContext.addDeal: usa cache global [...queryKeys.deals.lists(), 'view'] - DealsContext.addDeal: remove invalidateQueries prematura - useRealtimeSync: aplica updates diretamente na cache global - useMoveDeal: usa cache global para optimistic updates Melhorias de estabilidade: - Hydration fix em useResponsiveMode (inicializa com valor determinístico) - Guards em useBoardsController para evitar re-renders desnecessários - Normalização de campos snake_case -> camelCase no Realtime - Tolerância de 100ms para comparação de timestamps Outros: - Service Worker atualizado (v2) para forçar refresh - Telemetria condicional (só em dev) - Changelog atualizado Fixes: deal desaparece após criação, deal volta ao estágio original Co-authored-by: Thales Laray <thaleslaray@gmail.com>
1 parent f1d7af2 commit 3753761

File tree

23 files changed

+843
-573
lines changed

23 files changed

+843
-573
lines changed

app/(protected)/layout.tsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use client'
22

33
import { usePathname } from 'next/navigation'
4+
import { useEffect } from 'react'
45

56
import { QueryProvider } from '@/lib/query'
67
import { ToastProvider } from '@/context/ToastContext'
@@ -30,6 +31,45 @@ export default function ProtectedLayout({
3031
const isLabsRoute = pathname === '/labs' || pathname.startsWith('/labs/')
3132
const shouldUseAppShell = !isSetupRoute && !isLabsRoute
3233

34+
// #region agent log
35+
useEffect(() => {
36+
if (process.env.NODE_ENV !== 'production' && typeof window !== 'undefined') {
37+
const detectEnv = async () => {
38+
const env = {
39+
userAgent: navigator.userAgent,
40+
isCursorBrowser: navigator.userAgent.includes('Cursor') || window.location.hostname === 'localhost',
41+
hasServiceWorker: 'serviceWorker' in navigator,
42+
serviceWorkerReady: false,
43+
cacheAvailable: 'caches' in window,
44+
devToolsOpen: false,
45+
reactDevTools: !!(window as any).__REACT_DEVTOOLS_GLOBAL_HOOK__,
46+
localStorageAvailable: typeof Storage !== 'undefined',
47+
sessionStorageAvailable: typeof sessionStorage !== 'undefined',
48+
};
49+
50+
// Check service worker
51+
if (env.hasServiceWorker) {
52+
try {
53+
const registration = await navigator.serviceWorker.getRegistration();
54+
env.serviceWorkerReady = !!registration;
55+
} catch {}
56+
}
57+
58+
// Detect DevTools (heuristic)
59+
let devtools = false;
60+
const threshold = 160;
61+
const widthThreshold = window.outerWidth - window.innerWidth > threshold;
62+
const heightThreshold = window.outerHeight - window.innerHeight > threshold;
63+
devtools = widthThreshold || heightThreshold;
64+
env.devToolsOpen = devtools;
65+
66+
fetch('http://127.0.0.1:7242/ingest/d70f541c-09d7-4128-9745-93f15f184017',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'env-detect',hypothesisId:'ENV1',location:'app/(protected)/layout.tsx:ProtectedLayout',message:'Environment detection',data:env,timestamp:Date.now()})}).catch(()=>{});
67+
};
68+
detectEnv();
69+
}
70+
}, [pathname]);
71+
// #endregion
72+
3373
return (
3474
<QueryProvider>
3575
<ToastProvider>

components/pwa/ServiceWorkerRegister.tsx

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,37 @@ export function ServiceWorkerRegister() {
99

1010
const register = async () => {
1111
try {
12-
await navigator.serviceWorker.register('/sw.js');
13-
} catch {
12+
const registration = await navigator.serviceWorker.register('/sw.js');
13+
// #region agent log
14+
if (process.env.NODE_ENV !== 'production') {
15+
fetch('http://127.0.0.1:7242/ingest/d70f541c-09d7-4128-9745-93f15f184017',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'sw-register',hypothesisId:'SW1',location:'components/pwa/ServiceWorkerRegister.tsx:register',message:'Service Worker registered',data:{scope:registration.scope,active:!!registration.active,installing:!!registration.installing,waiting:!!registration.waiting},timestamp:Date.now()})}).catch(()=>{});
16+
}
17+
// #endregion
18+
19+
// Monitor service worker updates
20+
registration.addEventListener('updatefound', () => {
21+
// #region agent log
22+
if (process.env.NODE_ENV !== 'production') {
23+
fetch('http://127.0.0.1:7242/ingest/d70f541c-09d7-4128-9745-93f15f184017',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'sw-update',hypothesisId:'SW2',location:'components/pwa/ServiceWorkerRegister.tsx:updatefound',message:'Service Worker update found',data:{scope:registration.scope},timestamp:Date.now()})}).catch(()=>{});
24+
}
25+
// #endregion
26+
});
27+
28+
// Check for existing service worker
29+
if (registration.active) {
30+
// #region agent log
31+
if (process.env.NODE_ENV !== 'production') {
32+
fetch('http://127.0.0.1:7242/ingest/d70f541c-09d7-4128-9745-93f15f184017',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'sw-active',hypothesisId:'SW3',location:'components/pwa/ServiceWorkerRegister.tsx:register',message:'Service Worker already active',data:{scope:registration.scope,state:registration.active.state},timestamp:Date.now()})}).catch(()=>{});
33+
}
34+
// #endregion
35+
}
36+
} catch (err) {
37+
// #region agent log
38+
if (process.env.NODE_ENV !== 'production') {
39+
const errMsg = (err instanceof Error ? err.message : String(err || '')).slice(0, 120);
40+
fetch('http://127.0.0.1:7242/ingest/d70f541c-09d7-4128-9745-93f15f184017',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'sw-error',hypothesisId:'SW4',location:'components/pwa/ServiceWorkerRegister.tsx:register',message:'Service Worker registration error',data:{errMsg},timestamp:Date.now()})}).catch(()=>{});
41+
}
42+
// #endregion
1443
// noop (PWA is best-effort)
1544
}
1645
};

context/activities/ActivitiesContext.tsx

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,6 @@ export const ActivitiesProvider: React.FC<{ children: ReactNode }> = ({ children
6161
console.error('Usuário não autenticado');
6262
return null;
6363
}
64-
65-
const t0 = Date.now();
66-
// #region agent log
67-
if (process.env.NODE_ENV !== 'production') {
68-
fetch('http://127.0.0.1:7242/ingest/d70f541c-09d7-4128-9745-93f15f184017',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'boards-activities-visibility-3',hypothesisId:'A10',location:'context/activities/ActivitiesContext.tsx:addActivity',message:'ActivitiesContext.addActivity called',data:{hasProfile:true},timestamp:Date.now()})}).catch(()=>{});
69-
}
70-
// #endregion
7164
const { data, error: addError } = await activitiesService.create(activity);
7265

7366
if (addError) {
@@ -76,11 +69,6 @@ export const ActivitiesProvider: React.FC<{ children: ReactNode }> = ({ children
7669
}
7770

7871
// Invalida cache para TanStack Query atualizar
79-
// #region agent log
80-
if (process.env.NODE_ENV !== 'production') {
81-
fetch('http://127.0.0.1:7242/ingest/d70f541c-09d7-4128-9745-93f15f184017',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'boards-activities-visibility-3',hypothesisId:'A11',location:'context/activities/ActivitiesContext.tsx:addActivity',message:'ActivitiesContext.addActivity created; invalidating activities.all',data:{ms:Date.now()-t0},timestamp:Date.now()})}).catch(()=>{});
82-
}
83-
// #endregion
8472
// Don't await invalidations — awaiting can block UI flows until heavy refetches finish.
8573
void queryClient.invalidateQueries({ queryKey: queryKeys.activities.all });
8674

context/deals/DealsContext.tsx

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,6 @@ export const DealsProvider: React.FC<{ children: ReactNode }> = ({ children }) =
7070
console.error('Usuário não autenticado');
7171
return null;
7272
}
73-
74-
const t0 = Date.now();
75-
// #region agent log
76-
if (process.env.NODE_ENV !== 'production') {
77-
fetch('http://127.0.0.1:7242/ingest/d70f541c-09d7-4128-9745-93f15f184017',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'ux-lag-board-deal',hypothesisId:'D5',location:'context/deals/DealsContext.tsx:addDeal',message:'DealsContext.addDeal called',data:{boardId8:(deal.boardId||'').slice(0,8)||null},timestamp:Date.now()})}).catch(()=>{});
78-
}
79-
// #endregion
8073
const { data, error: addError } = await dealsService.create(deal);
8174

8275
if (addError) {
@@ -85,11 +78,6 @@ export const DealsProvider: React.FC<{ children: ReactNode }> = ({ children }) =
8578
}
8679

8780
// Invalida cache para TanStack Query atualizar
88-
// #region agent log
89-
if (process.env.NODE_ENV !== 'production') {
90-
fetch('http://127.0.0.1:7242/ingest/d70f541c-09d7-4128-9745-93f15f184017',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'ux-lag-board-deal',hypothesisId:'D6',location:'context/deals/DealsContext.tsx:addDeal',message:'DealsContext.addDeal created; invalidating deals.all',data:{ms:Date.now()-t0},timestamp:Date.now()})}).catch(()=>{});
91-
}
92-
// #endregion
9381
// Important: don't await invalidations. Awaiting can block UI flows until heavy refetches finish.
9482
queryClient.invalidateQueries({ queryKey: queryKeys.deals.all });
9583
queryClient.invalidateQueries({ queryKey: queryKeys.dashboard.stats });

context/settings/SettingsContext.tsx

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -201,13 +201,9 @@ export const SettingsProvider: React.FC<{ children: ReactNode }> = ({ children }
201201
// Config org-wide (fonte de verdade): provider/model/keys em organization_settings
202202
// Keep this eager: other parts of the UI need to know if AI is enabled / key configured.
203203
if (aiConfigLoadedForUserRef.current !== profile.id) {
204-
const t0 = Date.now();
205204
// Mark as "in-flight" immediately to avoid duplicate requests in dev StrictMode
206205
// where effects can run twice before the first request completes.
207206
aiConfigLoadedForUserRef.current = profile.id;
208-
// #region agent log
209-
fetch('http://127.0.0.1:7242/ingest/d70f541c-09d7-4128-9745-93f15f184017',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'settings-ai-load',hypothesisId:'AISET1',location:'context/settings/SettingsContext.tsx:fetchSettings',message:'Fetching /api/settings/ai (eager)',data:{path:(pathname||'').slice(0,40)||'/',userId8:profile.id.slice(0,8)},timestamp:Date.now()})}).catch(()=>{});
210-
// #endregion
211207
try {
212208
const aiRes = await fetch('/api/settings/ai', {
213209
method: 'GET',
@@ -249,17 +245,7 @@ export const SettingsProvider: React.FC<{ children: ReactNode }> = ({ children }
249245
aiConfigLoadedForUserRef.current = null;
250246
throw e;
251247
}
252-
// #region agent log
253-
if (process.env.NODE_ENV !== 'production') {
254-
fetch('http://127.0.0.1:7242/ingest/d70f541c-09d7-4128-9745-93f15f184017',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'settings-ai-load',hypothesisId:'AISET1',location:'context/settings/SettingsContext.tsx:fetchSettings',message:'Finished /api/settings/ai (eager)',data:{ms:Date.now()-t0},timestamp:Date.now()})}).catch(()=>{});
255-
}
256-
// #endregion
257248
} else {
258-
// #region agent log
259-
if (process.env.NODE_ENV !== 'production') {
260-
fetch('http://127.0.0.1:7242/ingest/d70f541c-09d7-4128-9745-93f15f184017',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'settings-ai-load',hypothesisId:'AISET1',location:'context/settings/SettingsContext.tsx:fetchSettings',message:'Skipped /api/settings/ai (already loaded for user)',data:{userId8:profile.id.slice(0,8)},timestamp:Date.now()})}).catch(()=>{});
261-
}
262-
// #endregion
263249
}
264250

265251
// Fetch lifecycle stages
@@ -288,13 +274,6 @@ export const SettingsProvider: React.FC<{ children: ReactNode }> = ({ children }
288274
if (!shouldLoadAiFeatures) return;
289275
if (aiFeaturesLoadedForUserRef.current === profile.id) return;
290276

291-
const t0 = Date.now();
292-
// #region agent log
293-
if (process.env.NODE_ENV !== 'production') {
294-
fetch('http://127.0.0.1:7242/ingest/d70f541c-09d7-4128-9745-93f15f184017',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'settings-ai-load',hypothesisId:'AISET2',location:'context/settings/SettingsContext.tsx:useEffect(ai-features)',message:'Fetching /api/settings/ai-features (lazy)',data:{path:(pathname||'').slice(0,40)||'/',isGlobalAIOpen,userId8:profile.id.slice(0,8)},timestamp:Date.now()})}).catch(()=>{});
295-
}
296-
// #endregion
297-
298277
// Mark as in-flight immediately to prevent duplicate requests in dev StrictMode.
299278
aiFeaturesLoadedForUserRef.current = profile.id;
300279

@@ -317,11 +296,6 @@ export const SettingsProvider: React.FC<{ children: ReactNode }> = ({ children }
317296
// Allow retry on transient errors
318297
aiFeaturesLoadedForUserRef.current = null;
319298
} finally {
320-
// #region agent log
321-
if (process.env.NODE_ENV !== 'production') {
322-
fetch('http://127.0.0.1:7242/ingest/d70f541c-09d7-4128-9745-93f15f184017',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'settings-ai-load',hypothesisId:'AISET2',location:'context/settings/SettingsContext.tsx:useEffect(ai-features)',message:'Finished /api/settings/ai-features (lazy)',data:{ms:Date.now()-t0},timestamp:Date.now()})}).catch(()=>{});
323-
}
324-
// #endregion
325299
}
326300
})();
327301
}, [profile, pathname, isGlobalAIOpen, shouldLoadAiFeatures]);

features/activities/hooks/useActivitiesController.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,6 @@ export const useActivitiesController = () => {
170170

171171
const handleSubmit = (e: React.FormEvent) => {
172172
e.preventDefault();
173-
const t0 = Date.now();
174-
// #region agent log
175-
if (process.env.NODE_ENV !== 'production') {
176-
fetch('http://127.0.0.1:7242/ingest/d70f541c-09d7-4128-9745-93f15f184017',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'boards-activities-visibility-2',hypothesisId:'A5',location:'features/activities/hooks/useActivitiesController.ts:handleSubmit',message:'Activities handleSubmit called',data:{mode:editingActivity?'edit':'create',hasDealId:!!formData.dealId},timestamp:Date.now()})}).catch(()=>{});
177-
}
178-
// #endregion
179173

180174
const date = new Date(`${formData.date}T${formData.time}`);
181175
const selectedDeal = formData.dealId ? dealsById.get(formData.dealId) : undefined;
@@ -224,20 +218,10 @@ export const useActivitiesController = () => {
224218
},
225219
{
226220
onSuccess: () => {
227-
// #region agent log
228-
if (process.env.NODE_ENV !== 'production') {
229-
fetch('http://127.0.0.1:7242/ingest/d70f541c-09d7-4128-9745-93f15f184017',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'boards-activities-visibility-2',hypothesisId:'A6',location:'features/activities/hooks/useActivitiesController.ts:handleSubmit:onSuccess',message:'Create activity success (controller)',data:{ms:Date.now()-t0},timestamp:Date.now()})}).catch(()=>{});
230-
}
231-
// #endregion
232221
showToast('Atividade criada com sucesso', 'success');
233222
setIsModalOpen(false);
234223
},
235224
onError: (error: Error) => {
236-
// #region agent log
237-
if (process.env.NODE_ENV !== 'production') {
238-
fetch('http://127.0.0.1:7242/ingest/d70f541c-09d7-4128-9745-93f15f184017',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'boards-activities-visibility-2',hypothesisId:'A6',location:'features/activities/hooks/useActivitiesController.ts:handleSubmit:onError',message:'Create activity error (controller)',data:{ms:Date.now()-t0,error:String(error?.message||'').split('\n')[0].slice(0,120)},timestamp:Date.now()})}).catch(()=>{});
239-
}
240-
// #endregion
241225
showToast(`Erro ao criar atividade: ${error.message}`, 'error');
242226
},
243227
}

0 commit comments

Comments
 (0)