Skip to content

feat(scheduled-tasks): replace the schedules table with calendar views#4979

Open
emir-karabeg wants to merge 5 commits into
stagingfrom
feat/calendar-tasks
Open

feat(scheduled-tasks): replace the schedules table with calendar views#4979
emir-karabeg wants to merge 5 commits into
stagingfrom
feat/calendar-tasks

Conversation

@emir-karabeg

Copy link
Copy Markdown
Collaborator

Summary

  • Replace the scheduled-tasks table with a calendar: month, week, and day views with a Today control, scope dropdown, chevron navigation, and a Google-Calendar-style current-time indicator on today's column. Week view is the default and Today scroll-centers the current time
  • Clicking a day or hour slot opens a create-task ChipModal (prompt textarea + chip date/time pickers, full width). Submit is a typed stub for now — the create contract requires a non-empty cronExpression, so one-time persistence lands in a follow-up. The grid components already accept eventsByDay/eventsByHour props so rendering real schedules as events only touches the container later
  • Calendar math is pure and unit-tested (calendar-grid.ts, schedule-events.ts, 12 tests); view state lives in a useCalendar hook, components follow the one-folder-per-component convention with barrels
  • Fix emcn DropdownMenu inside dialogs: a non-modal menu portals outside the dialog's react-remove-scroll subtree so its content can't be wheel-scrolled. ModalContent now marks its subtree via an InsideModal context and the menu root upgrades itself to modal inside dialogs; page-level menus keep their consumer-chosen modality. (Depends on the singleton dedupe shipped in fix(deps): dedupe radix focus-scope/dismissable-layer so in-modal dropdowns open #4977)
  • Resource shell simplifications: Resource.Table drops internal sorting and empty-state handling, the root becomes the overlay positioning context, toasts run independent timers with chip-aligned typography, breadcrumbs use the canonical loading placeholder
  • Rename the "Mothership" agent to "Sim" and the chat surface to "Chat" across landing copy, constitution, block metadata, and API error messages

Type of Change

  • New feature

Testing

  • bunx vitest run on the scheduled-tasks utils — 12/12 passing (grid derivation, anchor advancement, labels, event bucketing)
  • bunx tsc --noEmit clean; biome clean on all touched files
  • Manual: month/week/day navigation, Today centering, slot click → pre-filled modal, time dropdown opens and scrolls inside the modal, dark mode tokens

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Jun 12, 2026 5:21am

Request Review

@emir-karabeg

Copy link
Copy Markdown
Collaborator Author

@greptile run

@cursor

cursor Bot commented Jun 11, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Scheduled tasks loses list-based edit/pause/delete until calendar persistence and event injection ship, and Resource.Table no longer handles loading/empty/sort internally—callers must behave correctly on files, knowledge, logs, and tables pages.

Overview
Scheduled tasks drops the searchable table, filters, and full ScheduleModal CRUD (edit, pause, resume, delete) in favor of a month / week / day calendar with toolbar navigation, a live “now” line, and slot clicks that open a lightweight CreateTaskModal (submit is a stub; persistence and showing existing schedules on the grid are left for follow-up, though schedule-events helpers and eventsByDay/eventsByHour props are in place).

Resource shell changes: Resource.Table no longer sorts rows internally or shows built-in loading/empty states—pages keep headers visible and rely on Resource.Options for sort. Several workspace routes drop extra layout wrappers, use Resource for file/detail shells, and standardize breadcrumb while loading. Resource header fixes title truncation on load and hydration for the location-focus veil.

emcn: DropdownMenu forces modal behavior inside ModalContent (via InsideModalProvider) so menus scroll inside dialogs.

Product copy renames Mothership/Copilot to Sim (agent) and Chat (surface) across landing, docs, settings, blocks, APIs, and constitution; minor label fixes (e.g. incident.io).

Removes the Mod+E “clear notifications” global command from workspace permissions.

Reviewed by Cursor Bugbot for commit cdb3ae4. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit cdb3ae4. Configure here.

if (onSubmit) onSubmit(draft)
else logger.info('Scheduled task draft captured (not persisted this phase)', draft)
close()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modal form not reset on close

Medium Severity

Closing the create modal does not clear local prompt, launchDate, or launchTime. Reopening from the header keeps the same React key (none), so cancelled or submitted drafts reappear in the form.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cdb3ae4. Configure here.


const next = useCallback(() => setAnchor((current) => advanceAnchor(current, scope, 1)), [scope])
const prev = useCallback(() => setAnchor((current) => advanceAnchor(current, scope, -1)), [scope])
const goToday = useCallback(() => setAnchor(new Date()), [])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Today highlight uses stale date

Medium Severity

today is fixed at hook mount while goToday sets anchor to the current time. After midnight or a long session, Today navigation and isToday styling can disagree because the grid still compares days against the old today value.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cdb3ae4. Configure here.

@greptile-apps

greptile-apps Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces the scheduled-tasks table with a Google Calendar–style month/week/day calendar, fixes DropdownMenu scroll behavior inside dialogs via a new InsideModal context, simplifies the Resource shell, and renames "Mothership" to "Sim" across copy and metadata.

  • Calendar view: New ScheduleCalendar, MonthGrid, TimeGrid, and CalendarToolbar components derive their grids from a pure, unit-tested buildCalendarGrid utility. useCalendar owns ephemeral view state with a minute-interval today poll so the current-day highlight survives midnight correctly. Scroll centering is instant on mount/scope switch and smooth only on Today presses, matching the team's established rule.
  • DropdownMenu modal fix: ModalContent now wraps its subtree in InsideModalProvider; DropdownMenu reads this context and forces modal={true} inside dialogs, fixing scroll-lock contention between the dialog's react-remove-scroll layer and portaled non-modal menus.
  • Resource shell & stub task modal: Resource.Table drops internal sort and empty-state logic; the new TaskModal uses ChipModalPromptBody with the PromptEditor for a full-bleed chat-style scheduling surface, with persistence deferred to a follow-up once the one-time launch API contract is ready.

Confidence Score: 5/5

Safe to merge — no functional regressions found in the calendar logic, modal fix, or task management stub.

The calendar grid derivation is pure and backed by 12 passing unit tests. The InsideModal / DropdownMenu modal-upgrade fix is narrowly scoped and correctly leaves page-level menus untouched. The today poll in useCalendar correctly addresses the midnight-rollover issue raised in the previous review round. The task modal's submit guard (prompt non-empty AND launch time in the future) is validated at both render time and click time. No data loss, no broken auth paths, and no regressions in the shared Resource shell or emcn primitives were identified.

The empty-state regression in files.tsx (and knowledge/[id]/base.tsx) noted in the prior review round — blank table body on no-results filter — remains open; consumers of the simplified Resource.Table that relied on emptyMessage should wire their own empty state.

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/hooks/use-calendar.ts New hook owning ephemeral calendar view state; today is now state refreshed by a minute-interval isSameDay-guarded poll (fixing the previously-flagged stale-today issue), scroll signal correctly triggers smooth/instant scroll via a ref-compared counter.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/utils/calendar-grid.ts Pure calendar math (buildCalendarGrid, advanceAnchor, timeToOffset, formatScopeLabel); clock-free and fully unit-tested; no issues found.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/schedule-calendar/schedule-calendar.tsx Calendar body routing between MonthGrid and TimeGrid; scroll behavior correctly uses smooth for Today presses (scrollSignal delta) and instant for mount/scope switches, per team rule.
apps/sim/components/emcn/components/dropdown-menu/dropdown-menu.tsx DropdownMenu root now reads InsideModalContext and forces modal={true} inside dialogs, correctly fixing the react-remove-scroll scroll-lock conflict; page-level menus pass modal prop through untouched.
apps/sim/components/emcn/components/modal/modal-context.ts New InsideModalContext used to mark ModalContent subtrees; clean implementation with a boolean context and exported provider and hook.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/task-modal.tsx Stub scheduling modal using ChipModalPromptBody + PromptEditor; correctly guards submit on empty prompt and past launch time, seeds from slot or existing pending task, and shares edit/create flows cleanly.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/schedule-calendar/components/time-grid/time-grid.tsx TimeGrid with CurrentTimeIndicator; defers first render until after mount (avoiding SSR hydration mismatch), ticks every minute, correctly placed on today's column only via day.isToday; event chip click stopPropagation prevents double-opening.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/schedule-calendar/components/calendar-toolbar/calendar-toolbar.tsx New toolbar using ChipDatePicker (ghost variant) for period label and a DropdownMenu for scope. parseISO from date-fns v4 correctly treats date-only strings as local time, avoiding off-by-one in non-UTC timezones.
apps/sim/app/workspace/[workspaceId]/files/files.tsx Resource.Table now receives no emptyMessage; as flagged in prior review, empty search/filter results produce a silent blank table body with no user feedback.

Sequence Diagram

sequenceDiagram
    participant User
    participant CalendarToolbar
    participant ScheduleCalendar
    participant useCalendar
    participant TimeGrid/MonthGrid
    participant TaskModal
    participant useScheduledTasks

    User->>CalendarToolbar: Click Today
    CalendarToolbar->>ScheduleCalendar: handleToday()
    ScheduleCalendar->>useCalendar: goToday() setAnchor(new Date())
    ScheduleCalendar->>ScheduleCalendar: setScrollSignal(+1)
    ScheduleCalendar->>TimeGrid/MonthGrid: Smooth scroll to current time

    User->>TimeGrid/MonthGrid: Click hour slot
    TimeGrid/MonthGrid->>ScheduleCalendar: onSelectSlot(date, 14:00)
    ScheduleCalendar->>useCalendar: selectSlot(date, time)
    useCalendar-->>TaskModal: "open=true, slot={date, time}"
    User->>TaskModal: Type prompt, adjust date/time, click Schedule
    TaskModal->>useScheduledTasks: onSubmit(draft) addTask(draft)
    TaskModal->>useCalendar: closeCreate()

    User->>TimeGrid/MonthGrid: Right-click event chip
    TimeGrid/MonthGrid->>ScheduledTasks: onTaskContextMenu(task, e)
    ScheduledTasks->>TaskContextMenu: Show context menu
    User->>TaskContextMenu: Click Delete
    TaskContextMenu->>ScheduledTasks: setIsConfirmDeleteOpen(true)
    User->>ChipConfirmModal: Confirm
    ChipConfirmModal->>useScheduledTasks: deleteTask(id)
Loading

Reviews (3): Last reviewed commit: "feat(emcn): view-only field primitives +..." | Re-trigger Greptile

Comment thread apps/sim/app/workspace/[workspaceId]/scheduled-tasks/hooks/use-calendar.ts Outdated
@greptile-apps

greptile-apps Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces the scheduled-tasks table with a three-scope calendar (month/week/day), adds a ChipModal-based create-task form seeded from the clicked slot, simplifies the Resource shell (drops internal sort and empty-state handling from Resource.Table), fixes DropdownMenu scroll inside Radix dialogs via a new InsideModalContext, and renames "Mothership" to "Sim" across landing copy and API surfaces.

  • Calendar views: pure calendar-grid.ts/schedule-events.ts utilities with 12 unit tests power month/week/day grids; view state lives in useCalendar; a Google Calendar-style current-time indicator ticks every minute; event injection slots (eventsByDay/eventsByHour) are wired but intentionally empty until persistence ships.
  • Modal dropdown fix: DropdownMenu now reads InsideModalContext (set by ModalContent) and forces modal={true} inside dialogs, resolving the react-remove-scroll wheel-scroll incompatibility without affecting page-level menus.
  • Resource shell: Resource.Table drops emptyMessage, isLoading, defaultSort, and sortValues; the root gains position: relative as the overlay positioning context; consumers updated accordingly.

Confidence Score: 4/5

Safe to merge for the calendar UI and modal-dropdown fix; the today reference freezes at mount which breaks the today-highlight after midnight, and search/filter empty-state messages were removed without replacement.

The today value in useCalendar is computed once via useMemo(() => new Date(), []) and never refreshed. After midnight, goToday correctly advances anchor but today stays the old day, so buildCalendarGrid marks yesterday as isToday while the actual current day renders with no highlight. Separately, emptyMessage was removed from Resource.Table and consumers dropped the variable without adding replacement rendering, so users searching or filtering to zero results see a silent empty table.

apps/sim/app/workspace/[workspaceId]/scheduled-tasks/hooks/use-calendar.ts (stale today), apps/sim/app/workspace/[workspaceId]/files/files.tsx and apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx (missing empty-state feedback)

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/hooks/use-calendar.ts New hook for calendar view state; contains a stale today reference (frozen at mount via empty-dep useMemo) that will show the wrong day highlighted as "today" after midnight.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/utils/calendar-grid.ts Pure calendar grid derivation utilities (build/advance/format); well-structured with no side effects — fully deterministic and unit-tested.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/utils/schedule-events.ts Event adaptation and bucketing utilities; correct null/NaN guards on nextRunAt, consistent local-time bucketing aligned with the time grid display.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/scheduled-tasks.tsx Calendar page container; correctly wires useCalendar state to ScheduleCalendar and CreateTaskModal, slotKey re-seeds the modal on every new slot click.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/schedule-calendar/schedule-calendar.tsx Calendar body managing scroll and view dispatch; scroll-to-now logic may get a stale headerHeight on the first scope switch before the time-grid DOM is fully laid out.
apps/sim/components/emcn/components/dropdown-menu/dropdown-menu.tsx DropdownMenu now reads InsideModalContext and forces modal={true} inside Radix dialogs, fixing the wheel-scroll issue; page-level menus keep their consumer-set modality.
apps/sim/components/emcn/components/modal/modal-context.ts New InsideModalContext and useInsideModal hook; minimal, correctly implemented with a false default so non-modal trees are unaffected.
apps/sim/app/workspace/[workspaceId]/components/resource/resource.tsx Resource shell simplified: drops internal sort, emptyMessage, and isLoading from ResourceTable, making the root the overlay positioning context. Consumers that relied on emptyMessage for search/filter feedback now show silent empty tables.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/create-task-modal/create-task-modal.tsx New create-task modal using ChipModal; correctly stubs persistence (logs only), seeds date/time from the clicked slot, and disables Submit until prompt is non-empty.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/schedule-calendar/components/time-grid/time-grid.tsx Time grid for week/day scopes; CurrentTimeIndicator correctly defers to client-side only (avoiding hydration mismatch) and ticks every minute independently of the stale today reference.

Sequence Diagram

sequenceDiagram
    participant User
    participant ScheduledTasks
    participant useCalendar
    participant ScheduleCalendar
    participant CreateTaskModal

    User->>ScheduledTasks: mount
    ScheduledTasks->>useCalendar: "init (scope=week, anchor=now, today=now frozen)"
    useCalendar-->>ScheduledTasks: state
    ScheduledTasks->>ScheduleCalendar: render(scope, anchor, today)

    User->>ScheduleCalendar: click time slot
    ScheduleCalendar->>ScheduledTasks: onSelectSlot(date, time)
    ScheduledTasks->>useCalendar: selectSlot(date, time)
    useCalendar-->>ScheduledTasks: "selectedSlot set, isCreateOpen=true"
    ScheduledTasks->>CreateTaskModal: "open=true, slot"
    User->>CreateTaskModal: fill prompt, click Schedule
    CreateTaskModal->>CreateTaskModal: logger.info stub, no persistence
    CreateTaskModal-->>ScheduledTasks: onOpenChange(false)
    ScheduledTasks->>useCalendar: closeCreate()

    User->>ScheduleCalendar: click Today
    ScheduleCalendar->>ScheduledTasks: onToday()
    ScheduledTasks->>useCalendar: "goToday sets anchor=new Date()"
    ScheduleCalendar->>ScheduleCalendar: scrollSignal++ scrollTo(timeToOffset(now))
Loading

Comments Outside Diff (1)

  1. apps/sim/app/workspace/[workspaceId]/files/files.tsx, line 1890-1904 (link)

    P2 Empty-state feedback lost after search/filter

    emptyMessage was removed from ResourceTable without a replacement in consumer code. Before this PR, searching for a file that doesn't exist (e.g. a typo in the search box) or applying a filter that matches nothing would display a centred message ("No files match…" / "Nothing matches your filter"). Now rows = [] produces a silent empty table body — users get no indication of why the list is blank. The same regression appears in the Knowledge Base (knowledge/[id]/base.tsx).

Reviews (2): Last reviewed commit: "fix(emcn): force dropdown menus modal in..." | Re-trigger Greptile

Comment thread apps/sim/app/workspace/[workspaceId]/scheduled-tasks/hooks/use-calendar.ts Outdated
@emir-karabeg

Copy link
Copy Markdown
Collaborator Author

@greptile run

…dcrumbs

- Resource.Table: remove internal sorting (defaultSort/sortValues) and the
  emptyMessage state — rows render in the order given, chrome always paints
- Resource: root is now the positioning context for overlays; consumers
  (files, tables, knowledge, document) wrap detail views in <Resource>
  instead of hand-rolled divs
- ResourceHeader: root titles no longer truncate during initial layout;
  LocationFocusVeil gates the portal on mount to fix a hydration mismatch
- Toasts: drop the StackDismiss ring and stack countdown — each toast runs
  its own timer; remove the Mod+E clear-notifications command; align toast
  typography and icons with chip chrome
- Breadcrumbs: use the canonical '…' placeholder while names load
- incident.io: fix display name and catalog slug (with redirect)
- Add dev:capped / dev:full:capped scripts with a 4GB heap cap
Add month/time calendar views for scheduled tasks with toolbar, event
chips, and a create-task modal, backed by calendar-grid and
schedule-events utils (with tests) and a use-calendar hook. Replace the
old schedule-modal/context-menu flow.

Rename the "Mothership" agent to "Sim" and the chat surface to "Chat"
across landing copy, constitution, block metadata, API error messages,
and copilot/data-drain internals. Drop unused workspace route layouts.
A non-modal DropdownMenu portals outside an open dialog's
react-remove-scroll subtree, so its content cannot be wheel-scrolled
(e.g. the time picker in the scheduled-task create modal). ModalContent
now marks its subtree via an InsideModal context, and the emcn
DropdownMenu root upgrades itself to modal inside dialogs so it mounts
its own scroll lock and focus scope; page-level menus keep their
consumer-chosen modality.

Also stretch the create-task modal's date/time chip controls to full
width and drop the dead EDGE_GUTTER constant left behind by the
equal-tracks calendar layout.
…ck, smooth Today scroll

- useCalendar: today was frozen at mount, so after midnight the isToday
  column highlight and the current-time indicator stayed on the previous
  day. today is now state refreshed by a sleep-resilient minute poll
  that only re-renders when the calendar day actually changes
- CreateTaskModal: the stub submit closed silently, reading as false
  success; it now shows an info toast that the task was not created
- ScheduleCalendar: Today presses scroll smoothly as an orientation
  cue; mount and scope switches keep instant positioning
- ChipCopyInput (canonical view-only copy field), ChipTimePicker,
  ChipModalField type='copy', ChipTextarea viewOnly; new border chip
  variant and shared chipPrimaryFillTokens
- migrate ~40 consumers off disabled inputs and the deleted
  CopyableValueField; ChipConfirmModal description->text and
  secondaryActions[] API sweep
- scheduled-tasks: rename create-task-modal to task-modal, add
  task-details-modal + task-context-menu, useScheduledTasks hook
- home: extract prompt-editor (usePromptEditor) out of user-input
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant