Skip to content

Commit cd8c5bd

Browse files
waleedlatif1claude
andauthored
fix(logs): close sidebar when selected log disappears from filtered list + cleanup (#4186)
Derive sidebar open state from selection validity instead of using a separate useEffect. Also removes unnecessary useMemo/useCallback in non-memo'd components, replaces useEffect with render-time reset in dashboard, fixes CSS tokens, and adds hierarchical query key factory. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent f0285ad commit cd8c5bd

File tree

9 files changed

+112
-155
lines changed

9 files changed

+112
-155
lines changed

apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/components/line-chart/line-chart.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ function LineChartComponent({
241241
)}
242242
style={{ width, height }}
243243
>
244-
<p className='text-muted-foreground text-sm'>No data</p>
244+
<p className='text-[var(--text-muted)] text-sm'>No data</p>
245245
</div>
246246
)
247247
}
@@ -256,7 +256,7 @@ function LineChartComponent({
256256
>
257257
{!hasExternalWrapper && (
258258
<div className='mb-3 flex items-center gap-3'>
259-
<h4 className='font-medium text-foreground text-sm'>{label}</h4>
259+
<h4 className='font-medium text-[var(--text-primary)] text-sm'>{label}</h4>
260260
{allSeries.length > 1 && (
261261
<div className='flex items-center gap-2'>
262262
{scaledSeries.slice(1).map((s) => {

apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/dashboard.tsx

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

3-
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
3+
import { memo, useCallback, useMemo, useRef, useState } from 'react'
44
import { Loader2 } from 'lucide-react'
55
import { useParams } from 'next/navigation'
66
import { useShallow } from 'zustand/react/shallow'
@@ -441,10 +441,13 @@ function DashboardInner({ stats, isLoading, error }: DashboardProps) {
441441
[]
442442
)
443443

444-
useEffect(() => {
445-
setSelectedSegments((prev) => (Object.keys(prev).length > 0 ? {} : prev))
446-
setLastAnchorIndices((prev) => (Object.keys(prev).length > 0 ? {} : prev))
447-
}, [stats, timeRange, workflowIds, searchQuery])
444+
const resetKey = `${JSON.stringify(stats?.workflows?.map((w) => w.workflowId))}-${timeRange}-${workflowIds.join(',')}-${searchQuery}`
445+
const prevResetKeyRef = useRef(resetKey)
446+
if (resetKey !== prevResetKeyRef.current) {
447+
prevResetKeyRef.current = resetKey
448+
if (Object.keys(selectedSegments).length > 0) setSelectedSegments({})
449+
if (Object.keys(lastAnchorIndices).length > 0) setLastAnchorIndices({})
450+
}
448451

449452
if (isLoading) {
450453
return <DashboardSkeleton />

apps/sim/app/workspace/[workspaceId]/logs/components/log-details/log-details.tsx

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -296,13 +296,10 @@ export const LogDetails = memo(function LogDetails({
296296
}
297297
}, [log?.id])
298298

299-
const isWorkflowExecutionLog = useMemo(() => {
300-
if (!log) return false
301-
return (
302-
(log.trigger === 'manual' && !!log.duration) ||
303-
(log.executionData?.enhanced && log.executionData?.traceSpans)
304-
)
305-
}, [log])
299+
const isWorkflowExecutionLog =
300+
!!log &&
301+
((log.trigger === 'manual' && !!log.duration) ||
302+
!!(log.executionData?.enhanced && log.executionData?.traceSpans))
306303

307304
const hasCostInfo = isWorkflowExecutionLog && log?.cost
308305

@@ -337,10 +334,7 @@ export const LogDetails = memo(function LogDetails({
337334
return () => window.removeEventListener('keydown', handleKeyDown)
338335
}, [isOpen, onClose, hasPrev, hasNext, onNavigatePrev, onNavigateNext])
339336

340-
const formattedTimestamp = useMemo(
341-
() => (log ? formatDate(log.createdAt) : null),
342-
[log?.createdAt]
343-
)
337+
const formattedTimestamp = log ? formatDate(log.createdAt) : null
344338

345339
const logStatus = getDisplayStatus(log?.status)
346340

apps/sim/app/workspace/[workspaceId]/logs/components/logs-list/logs-list.tsx

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,14 @@ const LogRow = memo(
5858
? DELETED_WORKFLOW_COLOR
5959
: log.workflow?.color
6060

61-
const handleClick = useCallback(() => onClick(log), [onClick, log])
62-
63-
const handleMouseEnter = useCallback(() => onHover?.(log), [onHover, log])
64-
65-
const handleContextMenu = useCallback(
66-
(e: React.MouseEvent) => {
67-
if (onContextMenu) {
68-
e.preventDefault()
69-
onContextMenu(e, log)
70-
}
71-
},
72-
[onContextMenu, log]
73-
)
61+
const handleClick = () => onClick(log)
62+
const handleMouseEnter = () => onHover?.(log)
63+
const handleContextMenu = (e: React.MouseEvent) => {
64+
if (onContextMenu) {
65+
e.preventDefault()
66+
onContextMenu(e, log)
67+
}
68+
}
7469

7570
return (
7671
<div

apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/components/workflow-selector/workflow-selector.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,7 @@ export function WorkflowSelector({
3838
* When allWorkflows is true, pass empty array so the "All" option is selected.
3939
* Otherwise, pass the selected workflow IDs.
4040
*/
41-
const currentValues = useMemo(() => {
42-
return allWorkflows ? [] : selectedIds
43-
}, [allWorkflows, selectedIds])
41+
const currentValues = allWorkflows ? [] : selectedIds
4442

4543
/**
4644
* Handle multi-select changes from Combobox.

apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/notifications.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,9 @@ export const NotificationSettings = memo(function NotificationSettings({
197197
// Show form if user explicitly opened it OR if loading is complete with no subscriptions
198198
const displayForm = showForm || (!isLoading && !hasSubscriptions && !editingId)
199199

200-
const getSubscriptionsForTab = useCallback(
201-
(tab: NotificationType) => {
202-
return subscriptions.filter((s) => s.notificationType === tab)
203-
},
204-
[subscriptions]
205-
)
200+
const getSubscriptionsForTab = (tab: NotificationType) => {
201+
return subscriptions.filter((s) => s.notificationType === tab)
202+
}
206203

207204
const resetForm = useCallback(() => {
208205
setFormData({

apps/sim/app/workspace/[workspaceId]/logs/logs.tsx

Lines changed: 81 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,8 @@ export default function Logs() {
568568
}
569569
}, [selectedLogId, selectedLogIndex])
570570

571+
const effectiveSidebarOpen = isSidebarOpen && selectedLogIndex !== -1
572+
571573
const handleRefresh = useCallback(() => {
572574
setIsVisuallyRefreshing(true)
573575
const timerId = window.setTimeout(() => {
@@ -777,7 +779,7 @@ export default function Logs() {
777779
() => (
778780
<LogDetails
779781
log={selectedLog}
780-
isOpen={isSidebarOpen}
782+
isOpen={effectiveSidebarOpen}
781783
onClose={handleCloseSidebar}
782784
onNavigateNext={handleNavigateNext}
783785
onNavigatePrev={handleNavigatePrev}
@@ -787,7 +789,7 @@ export default function Logs() {
787789
),
788790
[
789791
selectedLog,
790-
isSidebarOpen,
792+
effectiveSidebarOpen,
791793
handleCloseSidebar,
792794
handleNavigateNext,
793795
handleNavigatePrev,
@@ -1260,20 +1262,10 @@ function LogsFilterPanel({ searchQuery, onSearchQueryChange }: LogsFilterPanelPr
12601262
const { data: folders = {} } = useFolderMap(workspaceId)
12611263
const { data: allWorkflowList = [] } = useWorkflows(workspaceId)
12621264

1263-
const workflows = useMemo(
1264-
() => allWorkflowList.map((w) => ({ id: w.id, name: w.name, color: w.color })),
1265-
[allWorkflowList]
1266-
)
1265+
const workflows = allWorkflowList.map((w) => ({ id: w.id, name: w.name, color: w.color }))
1266+
const folderList = Object.values(folders).filter((f) => f.workspaceId === workspaceId)
12671267

1268-
const folderList = useMemo(
1269-
() => Object.values(folders).filter((f) => f.workspaceId === workspaceId),
1270-
[folders, workspaceId]
1271-
)
1272-
1273-
const selectedStatuses = useMemo((): string[] => {
1274-
if (level === 'all' || !level) return []
1275-
return level.split(',').filter(Boolean)
1276-
}, [level])
1268+
const selectedStatuses = level === 'all' || !level ? [] : level.split(',').filter(Boolean)
12771269

12781270
const statusOptions: ComboboxOption[] = useMemo(
12791271
() =>
@@ -1285,58 +1277,46 @@ function LogsFilterPanel({ searchQuery, onSearchQueryChange }: LogsFilterPanelPr
12851277
[]
12861278
)
12871279

1288-
const handleStatusChange = useCallback(
1289-
(values: string[]) => {
1290-
setLevel(values.length === 0 ? 'all' : values.join(','))
1291-
},
1292-
[setLevel]
1293-
)
1294-
1295-
const statusDisplayLabel = useMemo(() => {
1296-
if (selectedStatuses.length === 0) return 'Status'
1297-
if (selectedStatuses.length === 1) {
1298-
const status = statusOptions.find((s) => s.value === selectedStatuses[0])
1299-
return status?.label || '1 selected'
1300-
}
1301-
return `${selectedStatuses.length} selected`
1302-
}, [selectedStatuses, statusOptions])
1303-
1304-
const selectedStatusColor = useMemo(() => {
1305-
if (selectedStatuses.length !== 1) return null
1306-
const status = selectedStatuses[0] as LogStatus
1307-
return STATUS_CONFIG[status]?.color ?? null
1308-
}, [selectedStatuses])
1309-
1310-
const workflowOptions: ComboboxOption[] = useMemo(
1311-
() => workflows.map((w) => ({ value: w.id, label: w.name, icon: getColorIcon(w.color, true) })),
1312-
[workflows]
1313-
)
1280+
const handleStatusChange = (values: string[]) => {
1281+
setLevel(values.length === 0 ? 'all' : values.join(','))
1282+
}
13141283

1315-
const workflowDisplayLabel = useMemo(() => {
1316-
if (workflowIds.length === 0) return 'Workflow'
1317-
if (workflowIds.length === 1) {
1318-
const workflow = workflows.find((w) => w.id === workflowIds[0])
1319-
return workflow?.name || '1 selected'
1320-
}
1321-
return `${workflowIds.length} workflows`
1322-
}, [workflowIds, workflows])
1284+
const statusDisplayLabel =
1285+
selectedStatuses.length === 0
1286+
? 'Status'
1287+
: selectedStatuses.length === 1
1288+
? statusOptions.find((s) => s.value === selectedStatuses[0])?.label || '1 selected'
1289+
: `${selectedStatuses.length} selected`
1290+
1291+
const selectedStatusColor =
1292+
selectedStatuses.length === 1
1293+
? (STATUS_CONFIG[selectedStatuses[0] as LogStatus]?.color ?? null)
1294+
: null
1295+
1296+
const workflowOptions: ComboboxOption[] = workflows.map((w) => ({
1297+
value: w.id,
1298+
label: w.name,
1299+
icon: getColorIcon(w.color, true),
1300+
}))
1301+
1302+
const workflowDisplayLabel =
1303+
workflowIds.length === 0
1304+
? 'Workflow'
1305+
: workflowIds.length === 1
1306+
? workflows.find((w) => w.id === workflowIds[0])?.name || '1 selected'
1307+
: `${workflowIds.length} workflows`
13231308

13241309
const selectedWorkflow =
13251310
workflowIds.length === 1 ? workflows.find((w) => w.id === workflowIds[0]) : null
13261311

1327-
const folderOptions: ComboboxOption[] = useMemo(
1328-
() => folderList.map((f) => ({ value: f.id, label: f.name })),
1329-
[folderList]
1330-
)
1312+
const folderOptions: ComboboxOption[] = folderList.map((f) => ({ value: f.id, label: f.name }))
13311313

1332-
const folderDisplayLabel = useMemo(() => {
1333-
if (folderIds.length === 0) return 'Folder'
1334-
if (folderIds.length === 1) {
1335-
const folder = folderList.find((f) => f.id === folderIds[0])
1336-
return folder?.name || '1 selected'
1337-
}
1338-
return `${folderIds.length} folders`
1339-
}, [folderIds, folderList])
1314+
const folderDisplayLabel =
1315+
folderIds.length === 0
1316+
? 'Folder'
1317+
: folderIds.length === 1
1318+
? folderList.find((f) => f.id === folderIds[0])?.name || '1 selected'
1319+
: `${folderIds.length} folders`
13401320

13411321
const triggerOptions: ComboboxOption[] = useMemo(
13421322
() =>
@@ -1348,69 +1328,57 @@ function LogsFilterPanel({ searchQuery, onSearchQueryChange }: LogsFilterPanelPr
13481328
[]
13491329
)
13501330

1351-
const triggerDisplayLabel = useMemo(() => {
1352-
if (triggers.length === 0) return 'Trigger'
1353-
if (triggers.length === 1) {
1354-
const trigger = triggerOptions.find((t) => t.value === triggers[0])
1355-
return trigger?.label || '1 selected'
1356-
}
1357-
return `${triggers.length} triggers`
1358-
}, [triggers, triggerOptions])
1359-
1360-
const timeDisplayLabel = useMemo(() => {
1361-
if (timeRange === 'All time') return 'Time'
1362-
if (timeRange === 'Custom range' && startDate && endDate) {
1363-
return `${formatDateShort(startDate)} - ${formatDateShort(endDate)}`
1331+
const triggerDisplayLabel =
1332+
triggers.length === 0
1333+
? 'Trigger'
1334+
: triggers.length === 1
1335+
? triggerOptions.find((t) => t.value === triggers[0])?.label || '1 selected'
1336+
: `${triggers.length} triggers`
1337+
1338+
const timeDisplayLabel =
1339+
timeRange === 'All time'
1340+
? 'Time'
1341+
: timeRange === 'Custom range' && startDate && endDate
1342+
? `${formatDateShort(startDate)} - ${formatDateShort(endDate)}`
1343+
: timeRange === 'Custom range'
1344+
? 'Custom range'
1345+
: timeRange
1346+
1347+
const handleTimeRangeChange = (val: string) => {
1348+
if (val === 'Custom range') {
1349+
setPreviousTimeRange(timeRange)
1350+
setDatePickerOpen(true)
1351+
} else {
1352+
clearDateRange()
1353+
setTimeRange(val as typeof timeRange)
13641354
}
1365-
if (timeRange === 'Custom range') return 'Custom range'
1366-
return timeRange
1367-
}, [timeRange, startDate, endDate])
1368-
1369-
const handleTimeRangeChange = useCallback(
1370-
(val: string) => {
1371-
if (val === 'Custom range') {
1372-
setPreviousTimeRange(timeRange)
1373-
setDatePickerOpen(true)
1374-
} else {
1375-
clearDateRange()
1376-
setTimeRange(val as typeof timeRange)
1377-
}
1378-
},
1379-
[timeRange, setTimeRange, clearDateRange]
1380-
)
1355+
}
13811356

1382-
const handleDateRangeApply = useCallback(
1383-
(start: string, end: string) => {
1384-
setDateRange(start, end)
1385-
setDatePickerOpen(false)
1386-
},
1387-
[setDateRange]
1388-
)
1357+
const handleDateRangeApply = (start: string, end: string) => {
1358+
setDateRange(start, end)
1359+
setDatePickerOpen(false)
1360+
}
13891361

1390-
const handleDatePickerCancel = useCallback(() => {
1362+
const handleDatePickerCancel = () => {
13911363
if (timeRange === 'Custom range' && !startDate) {
13921364
setTimeRange(previousTimeRange)
13931365
}
13941366
setDatePickerOpen(false)
1395-
}, [timeRange, startDate, previousTimeRange, setTimeRange])
1367+
}
13961368

1397-
const filtersActive = useMemo(
1398-
() =>
1399-
hasActiveFilters({
1400-
timeRange,
1401-
level,
1402-
workflowIds,
1403-
folderIds,
1404-
triggers,
1405-
searchQuery,
1406-
}),
1407-
[timeRange, level, workflowIds, folderIds, triggers, searchQuery]
1408-
)
1369+
const filtersActive = hasActiveFilters({
1370+
timeRange,
1371+
level,
1372+
workflowIds,
1373+
folderIds,
1374+
triggers,
1375+
searchQuery,
1376+
})
14091377

1410-
const handleClearFilters = useCallback(() => {
1378+
const handleClearFilters = () => {
14111379
resetFilters()
14121380
onSearchQueryChange('')
1413-
}, [resetFilters, onSearchQueryChange])
1381+
}
14141382

14151383
return (
14161384
<div className='flex w-[240px] flex-col gap-3 p-3'>

0 commit comments

Comments
 (0)