feat(scheduled-tasks): pause/resume, mutation toasts, submit guards, empty state#5044
Conversation
…empty state - Toasts on every job mutation (create/update/delete/exclude/pause/resume) so failures are never silent - TaskModal stays open until the save persists; submit disabled in-flight to block double-submit - Pause/Resume recurring tasks via the context menu; paused occurrences render dimmed so they stay resumable - First-run empty state with a create CTA when the workspace has no tasks
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview The create/edit modal now awaits async saves: it stays open on failure, blocks dismiss and double-submit while in flight, and shows in-progress labels on the primary action. Create/update mutations use Schedule mutations surface success and error toasts (create, update, delete, exclude occurrence, pause, resume). General settings aligns Theme, Timezone, and Snap-to-grid dropdown triggers to a shared 240px width. Reviewed by Cursor Bugbot for commit a7624ee. Configure here. |
…its persistence The edit TaskModal's onSubmit called updateTask without returning its promise, so the await resolved immediately — the modal closed before the save finished, failed edits didn't stay open, and a rejected mutateAsync went unhandled. Return the promise so the await tracks the real mutation (matching the create path).
|
@greptile review |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit ac2b846. Configure here.
Greptile SummaryThis PR adds a control and observability layer to the scheduled-tasks module: mutation toasts on every job operation (create/update/delete/pause/resume/exclude), a submit guard that keeps the modal open on failure, pause/resume of recurring tasks via the context menu, and dimmed rendering for paused calendar occurrences.
Confidence Score: 5/5The PR is safe to merge — all mutation paths now surface errors via toast and the modal correctly guards against premature close and double-submit. The submit guard uses a well-reasoned stale-closure pattern that correctly allows programmatic success-close to pass through while blocking all user-initiated dismissals during flight. The mutateAsync to onError to catch chain produces exactly one toast on failure and leaves the modal open with the draft intact. The pause/resume feature is correctly gated to recurring pending tasks only, and paused occurrences continue to expand in the calendar with a disabled flag. The schedule-events logic change is covered by three new unit tests. No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant U as User
participant TM as TaskModal
participant HS as handleSubmit
participant HK as useScheduledTasks
participant SQ as schedules.ts
participant API as API
U->>TM: Click Schedule or Save
TM->>HS: handleSubmit()
HS->>HK: createTask or updateTask
HK->>SQ: mutateAsync
SQ->>API: HTTP request
alt Success
API-->>SQ: 200 OK
SQ->>SQ: onSuccess toast.success
SQ-->>HS: resolves
HS->>TM: close via stale closure
TM-->>U: Modal closes
else Failure
API-->>SQ: Error
SQ->>SQ: onError toast.error
SQ-->>HS: catch returns false
HS-->>TM: stays open
TM-->>U: Modal open with toast
end
Reviews (10): Last reviewed commit: "fix(scheduled-tasks): gate submit on a s..." | Re-trigger Greptile |
|
@greptile review |
|
@cursor review |
Greptile SummaryThis PR adds a control layer to the scheduled-tasks module: toast feedback on every job mutation, an async submit guard in the create/edit modal (stays open until the task persists, closes only on success), Pause/Resume for recurring tasks via the existing
Confidence Score: 4/5The change is well-scoped and the core Pause/Resume and toast wiring is correct; the only rough edge is the missing The submit guard is the riskiest new surface:
Important Files Changed
Sequence DiagramsequenceDiagram
participant U as User
participant TM as TaskModal
participant UST as useScheduledTasks
participant SQ as schedules.ts (React Query)
participant API as API
U->>TM: clicks Schedule / Save
TM->>TM: setSubmitting(true)
TM->>UST: onSubmit(draft) → createTask / updateTask
UST->>SQ: mutateAsync(body)
SQ->>API: POST /schedules or PATCH /schedules/:id
API-->>SQ: 200 OK
SQ->>SQ: onSuccess → toast.success(...)
SQ-->>UST: Promise resolves
UST-->>TM: Promise resolves
TM->>TM: close() → onOpenChange(false)
Note right of TM: setSubmitting(false) never called on success path
alt API error
API-->>SQ: 4xx / 5xx
SQ->>SQ: onError → toast.error(...)
SQ-->>UST: Promise rejects
UST-->>TM: throws
TM->>TM: catch → setSubmitting(false)
Note right of TM: Modal stays open with draft intact
end
U->>TM: right-clicks task chip
TM->>UST: pauseTask(scheduleId) / resumeTask(scheduleId)
UST->>SQ: disableSchedule.mutate / resumeSchedule.mutate
UST->>UST: setSelectedTask(null) immediately
SQ->>API: PATCH /schedules/:id (disable/reactivate)
API-->>SQ: 200 OK
SQ->>SQ: onSuccess → toast.success(...)
SQ->>SQ: onSettled → invalidateQueries(list + details)
Reviews (2): Last reviewed commit: "refactor(scheduled-tasks): explicit retu..." | Re-trigger Greptile |
…guard - Seed the created job into the workspace-list cache on success so the new task renders instantly and the first-run empty state never flashes between the success toast and the list refetch; onSettled still reconciles authoritatively. - Reset the modal's submitting flag in a finally so the submit button can never stick disabled if the modal is kept open.
|
@greptile review |
|
@cursor review |
Remove the empty-state prompt and its supporting hasTasks flag; with the empty state gone, also revert the create optimistic-insert (its only purpose was avoiding the empty-state flash) back to plain toast + list invalidation.
|
@greptile review |
|
@cursor review |
Cancel, the header X, Escape, and overlay click all route through one guarded onOpenChange that no-ops while submitting, and the footer Cancel is disabled (cancelDisabled) — so an in-progress create/edit can't be abandoned mid-save and lose its draft. submitting moves up to TaskModal so the guard can read it.
Theme/Snap-to-grid (ChipSelect) hugged their content (~90px) while Timezone (ChipCombobox) was pinned to 260px, so the three read as a ragged column. Give all three a shared 240px trigger via fullWidth + a common width wrapper so they align as one column; menus match their triggers. No behavioral change — timezone keeps search, options and handlers are untouched.
|
@greptile review |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 79d40f7. Configure here.
|
@greptile review |
|
@cursor review |
Delete bypasses the modal's dismiss guard (it closes via closeTask, not onOpenChange), so a click mid-save could run a delete against the same task as the in-flight update. Disable it while submitting, matching Cancel.
|
@greptile review |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit c5d659d. Configure here.
Move the Delete-lock rationale to a TSDoc block on secondaryActions, and restructure handleSubmit to a boolean persist result so the failure path is self-evident — removing the inline comments and the empty catch block.
|
@greptile review |
|
@cursor review |
… double-submit The submitting state flag only reflects after a re-render, so two same-tick invocations (Enter racing the click) could both pass a state-based guard and fire two mutations. A submittingRef flips synchronously, so the second invocation is rejected before it can submit again; the state still drives the button/cancel UI.
|
@greptile review |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit a7624ee. Configure here.
Summary
disable/reactivateendpoints. Paused occurrences render dimmed so a suspended task stays visible and resumable instead of disappearing.Type of Change
Testing
tailwind.config.ts), biome clean,check:api-validationandcheck:react-querypassreactivateendpoint requires a cron)Checklist