Skip to content

Commit b1f1881

Browse files
committed
address more nits
1 parent b468c00 commit b1f1881

3 files changed

Lines changed: 138 additions & 4 deletions

File tree

apps/sim/app/workspace/[workspaceId]/components/resource/resource.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -495,17 +495,22 @@ const DataRow = memo(function DataRow({
495495
const isActiveDropTarget = rowDragDrop?.activeDropTargetId === row.id
496496
const isDragging = rowDragDrop?.draggedRowIds?.has(row.id) ?? false
497497
const isAnyDragActive = rowDragDrop?.isAnyDragActive ?? false
498+
const hasActiveSelection = (selectable?.selectedIds.size ?? 0) > 0
498499

499500
const handleClick = useCallback(
500501
(e: React.MouseEvent<HTMLTableRowElement>) => {
501-
if (selectable && !selectable.disabled && (e.shiftKey || !onRowClick)) {
502+
if (
503+
selectable &&
504+
!selectable.disabled &&
505+
(e.shiftKey || e.metaKey || e.ctrlKey || !onRowClick || hasActiveSelection)
506+
) {
502507
e.preventDefault()
503508
selectable.onSelectRow(row.id, !isSelected, e.shiftKey)
504509
return
505510
}
506511
onRowClick?.(row.id)
507512
},
508-
[isSelected, onRowClick, row.id, selectable]
513+
[hasActiveSelection, isSelected, onRowClick, row.id, selectable]
509514
)
510515

511516
const handleMouseEnter = useCallback(() => {
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/**
2+
* @vitest-environment node
3+
*/
4+
import { beforeEach, describe, expect, it, vi } from 'vitest'
5+
6+
const {
7+
mockSelect,
8+
mockUpdate,
9+
mockUpdateSet,
10+
mockUpdateWhere,
11+
mockRecordAudit,
12+
mockCaptureServerEvent,
13+
} = vi.hoisted(() => ({
14+
mockSelect: vi.fn(),
15+
mockUpdate: vi.fn(),
16+
mockUpdateSet: vi.fn(),
17+
mockUpdateWhere: vi.fn(),
18+
mockRecordAudit: vi.fn(),
19+
mockCaptureServerEvent: vi.fn(),
20+
}))
21+
22+
vi.mock('@sim/audit', () => ({
23+
AuditAction: { SCHEDULE_UPDATED: 'SCHEDULE_UPDATED' },
24+
AuditResourceType: { SCHEDULE: 'SCHEDULE' },
25+
recordAudit: mockRecordAudit,
26+
}))
27+
28+
vi.mock('@sim/db', () => ({
29+
db: {
30+
select: mockSelect,
31+
update: mockUpdate,
32+
},
33+
workflowSchedule: {
34+
id: 'id',
35+
sourceWorkspaceId: 'sourceWorkspaceId',
36+
sourceType: 'sourceType',
37+
archivedAt: 'archivedAt',
38+
timezone: 'timezone',
39+
status: 'status',
40+
cronExpression: 'cronExpression',
41+
jobTitle: 'jobTitle',
42+
},
43+
}))
44+
45+
vi.mock('@sim/logger', () => ({
46+
createLogger: () => ({
47+
error: vi.fn(),
48+
info: vi.fn(),
49+
warn: vi.fn(),
50+
}),
51+
}))
52+
53+
vi.mock('drizzle-orm', () => ({
54+
and: vi.fn(),
55+
eq: vi.fn(),
56+
isNull: vi.fn(),
57+
}))
58+
59+
vi.mock('@/lib/posthog/server', () => ({
60+
captureServerEvent: mockCaptureServerEvent,
61+
}))
62+
63+
import { performUpdateJob } from '@/lib/workflows/schedules/orchestration'
64+
65+
const BASE_JOB = {
66+
id: 'job-1',
67+
sourceWorkspaceId: 'workspace-1',
68+
sourceType: 'job',
69+
archivedAt: null,
70+
timezone: 'UTC',
71+
cronExpression: null,
72+
jobTitle: 'Nightly task',
73+
status: 'disabled',
74+
}
75+
76+
function mockExistingJob(job: typeof BASE_JOB) {
77+
mockSelect.mockReturnValue({
78+
from: vi.fn().mockReturnValue({
79+
where: vi.fn().mockReturnValue({
80+
limit: vi.fn().mockReturnValue([job]),
81+
}),
82+
}),
83+
})
84+
}
85+
86+
describe('performUpdateJob', () => {
87+
beforeEach(() => {
88+
vi.clearAllMocks()
89+
mockUpdateSet.mockReturnValue({ where: mockUpdateWhere })
90+
mockUpdate.mockReturnValue({ set: mockUpdateSet })
91+
mockUpdateWhere.mockResolvedValue(undefined)
92+
})
93+
94+
it('does not schedule a next run when editing time on a disabled job', async () => {
95+
mockExistingJob({ ...BASE_JOB, status: 'disabled' })
96+
97+
const result = await performUpdateJob({
98+
jobId: 'job-1',
99+
workspaceId: 'workspace-1',
100+
userId: 'user-1',
101+
time: '2099-01-01T09:00:00Z',
102+
})
103+
104+
expect(result.success).toBe(true)
105+
expect(mockUpdateSet).toHaveBeenCalledTimes(1)
106+
expect(mockUpdateSet.mock.calls[0][0]).not.toHaveProperty('nextRunAt')
107+
})
108+
109+
it('schedules the next run when editing time on an active job', async () => {
110+
mockExistingJob({ ...BASE_JOB, status: 'active' })
111+
112+
const result = await performUpdateJob({
113+
jobId: 'job-1',
114+
workspaceId: 'workspace-1',
115+
userId: 'user-1',
116+
time: '2099-01-01T09:00:00Z',
117+
})
118+
119+
expect(result.success).toBe(true)
120+
expect(mockUpdateSet).toHaveBeenCalledTimes(1)
121+
expect(mockUpdateSet.mock.calls[0][0]).toMatchObject({
122+
nextRunAt: new Date('2099-01-01T09:00:00Z'),
123+
})
124+
})
125+
})

apps/sim/lib/workflows/schedules/orchestration.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ export async function performUpdateJob(
267267
}
268268
if (params.successCondition !== undefined) updates.successCondition = params.successCondition
269269
if (params.maxRuns !== undefined) updates.maxRuns = params.maxRuns
270+
const effectiveStatus = updates.status ?? job.status
271+
270272
if (params.cronExpression !== undefined) {
271273
const timezone = params.timezone || job.timezone || 'UTC'
272274
const validation = validateCronExpression(params.cronExpression, timezone)
@@ -278,7 +280,7 @@ export async function performUpdateJob(
278280
}
279281
}
280282
updates.cronExpression = params.cronExpression
281-
if ((updates.status ?? job.status) === 'active') updates.nextRunAt = validation.nextRun
283+
if (effectiveStatus === 'active') updates.nextRunAt = validation.nextRun
282284
}
283285
if (params.time !== undefined && params.time !== null) {
284286
const timezone = params.timezone || job.timezone || 'UTC'
@@ -292,7 +294,9 @@ export async function performUpdateJob(
292294
}
293295
const cronExpression =
294296
params.cronExpression !== undefined ? params.cronExpression : job.cronExpression
295-
if (!cronExpression || parsed > new Date()) updates.nextRunAt = parsed
297+
if (effectiveStatus === 'active' && (!cronExpression || parsed > new Date())) {
298+
updates.nextRunAt = parsed
299+
}
296300
}
297301

298302
const updatedFields = Object.keys(updates).filter((key) => key !== 'updatedAt')

0 commit comments

Comments
 (0)