Skip to content

feat(schedule-actions): add action types, config, and menu#1419

Merged
Assem-Uber merged 12 commits into
cadence-workflow:masterfrom
Assem-Uber:feat/schedule-actions-pause-resume-ui
Jul 1, 2026
Merged

feat(schedule-actions): add action types, config, and menu#1419
Assem-Uber merged 12 commits into
cadence-workflow:masterfrom
Assem-Uber:feat/schedule-actions-pause-resume-ui

Conversation

@Assem-Uber

@Assem-Uber Assem-Uber commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

First PR in a stacked series that adds the schedule-actions UI (mirroring workflow-actions). Introduces the generic action contract, pause/resume config (including the pause warning banner config), disabled-reason helpers, and the popover menu.

Stack (merge in order):

PR Branch Scope
PR1 (this) feat/schedule-actions-pause-resume-ui Types, config, menu
PR2 feat/schedule-actions-modal Modal + mutation flow
PR3 feat/schedule-actions-mount Page wiring

Nothing is mounted on the schedule page until PR3. Banner config is defined here; modal rendering lands in PR2.

Changes

  • schedule-actions.types.ts — generic ScheduleAction contract, optional modal.banner
  • config/ — pause/resume config, banner message/constants, non-runnable status/reason maps
  • schedule-actions-menu/ — popover menu; actions disabled while schedule is loading
  • __fixtures__/schedule-actions-config.tsx — test fixtures

Testing

  • npm run typecheck
  • npm run test:unit:browser schedule-actions-menu get-action-disabled-reason

@Assem-Uber Assem-Uber force-pushed the feat/schedule-actions-pause-resume-ui branch from cc05155 to 7ff39fa Compare June 30, 2026 13:40
@Assem-Uber Assem-Uber changed the title feat(schedule-actions): pause/resume UI shell (SLICE-2) feat(schedule-actions): add action types, config, and menu (PR1) Jun 30, 2026
@Assem-Uber Assem-Uber force-pushed the feat/schedule-actions-pause-resume-ui branch 2 times, most recently from 381a55a to e72e21e Compare July 1, 2026 07:55
Add resolver, types, constants, and disabled-values config for the
SCHEDULE_ACTIONS_ENABLED feature flag. Wire it into dynamicConfigs
and the resolver schema registry.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Assem-Uber Assem-Uber force-pushed the feat/schedule-actions-pause-resume-ui branch from e72e21e to baeec07 Compare July 1, 2026 08:07
@gitar-bot

gitar-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown
Code Review ⚠️ Changes requested 1 resolved / 2 findings

Implements the foundational schedule-actions infrastructure including types, configurations, and the popover menu. The logic for disabling actions currently fails while the schedule is loading, as the early-return prevents the configuration check from executing.

⚠️ Bug: Config-disabled action appears enabled while schedule loads

📄 src/views/schedule-actions/schedule-actions-menu/helpers/get-action-disabled-reason.ts:14-26 📄 src/views/schedule-actions/schedule-actions-menu/schedule-actions-menu.tsx:18-23

In getActionDisabledReason, the if (!actionRunnableStatus) return undefined; early-return runs before the actionEnabledConfig !== 'ENABLED' check. In the menu (schedule-actions-menu.tsx:20-22), actionRunnableStatus is undefined whenever schedule is still loading. actionsEnabledConfig comes from a separate resolver (SCHEDULE_ACTIONS_ENABLED) and can already be resolved to a disabled value (e.g. DISABLED_UNAUTHORIZED / DISABLED_DEFAULT) while the schedule is still loading.

Result: during loading, an action that config says is disabled/unauthorized returns undefined (no disabled reason) → the button is rendered enabled and clickable (onClick={() => onActionSelect(action)}). A user could trigger an action they are not authorized for, or that is globally disabled, during the loading window. It also silently drops the disabled tooltip in that window.

The existing test returns disabled reason over non-runnable reason when both apply establishes that config-disabled is intended to take precedence, but that precedence is bypassed when actionRunnableStatus is undefined. No test covers the loading (actionRunnableStatus: undefined) + config-disabled combination.

Fix: evaluate the config-disabled check before the runnable-status guard so a disabled config is always honored regardless of load state.

Check config-disabled state before the runnable-status guard so a disabled/unauthorized config is honored even while the schedule is loading.
if (actionEnabledConfig !== 'ENABLED') {
  return SCHEDULE_ACTIONS_DISABLED_REASONS_CONFIG[
    actionEnabledConfig ?? 'DISABLED_DEFAULT'
  ];
}

if (!actionRunnableStatus) {
  return undefined;
}

if (actionRunnableStatus !== 'RUNNABLE') {
  return SCHEDULE_ACTIONS_NON_RUNNABLE_REASONS_CONFIG[actionRunnableStatus];
}

return undefined;
✅ 1 resolved
Edge Case: Both actions appear enabled while schedule is still loading

📄 src/views/schedule-actions/schedule-actions-menu/schedule-actions-menu.tsx:17-21 📄 src/views/schedule-actions/schedule-actions-menu/helpers/get-action-disabled-reason.ts:9-11
In schedule-actions-menu.tsx, the disabled reason is computed only when schedule is defined: actionRunnableStatus: schedule ? action.getRunnableStatus(schedule) : undefined. When schedule is undefined (still loading or transiently empty), getActionDisabledReason returns undefined, so BOTH the Pause and Resume buttons render as enabled — a state that should never be reachable since the two actions are meant to be mutually exclusive based on state.paused. The popover trigger button uses isLoading, which mitigates this in the common case, but if the menu is opened before describe data resolves the user could trigger an action against unknown schedule state. Consider disabling actions (or showing a generic loading/disabled reason) when schedule is undefined.

🤖 Prompt for agents
Code Review: Implements the foundational schedule-actions infrastructure including types, configurations, and the popover menu. The logic for disabling actions currently fails while the schedule is loading, as the early-return prevents the configuration check from executing.

1. ⚠️ Bug: Config-disabled action appears enabled while schedule loads
   Files: src/views/schedule-actions/schedule-actions-menu/helpers/get-action-disabled-reason.ts:14-26, src/views/schedule-actions/schedule-actions-menu/schedule-actions-menu.tsx:18-23

   In `getActionDisabledReason`, the `if (!actionRunnableStatus) return undefined;` early-return runs *before* the `actionEnabledConfig !== 'ENABLED'` check. In the menu (`schedule-actions-menu.tsx:20-22`), `actionRunnableStatus` is `undefined` whenever `schedule` is still loading. `actionsEnabledConfig` comes from a separate resolver (`SCHEDULE_ACTIONS_ENABLED`) and can already be resolved to a disabled value (e.g. `DISABLED_UNAUTHORIZED` / `DISABLED_DEFAULT`) while the schedule is still loading.
   
   Result: during loading, an action that config says is disabled/unauthorized returns `undefined` (no disabled reason) → the button is rendered enabled and clickable (`onClick={() => onActionSelect(action)}`). A user could trigger an action they are not authorized for, or that is globally disabled, during the loading window. It also silently drops the disabled tooltip in that window.
   
   The existing test `returns disabled reason over non-runnable reason when both apply` establishes that config-disabled is intended to take precedence, but that precedence is bypassed when `actionRunnableStatus` is undefined. No test covers the loading (`actionRunnableStatus: undefined`) + config-disabled combination.
   
   Fix: evaluate the config-disabled check before the runnable-status guard so a disabled config is always honored regardless of load state.

   Fix (Check config-disabled state before the runnable-status guard so a disabled/unauthorized config is honored even while the schedule is loading.):
   if (actionEnabledConfig !== 'ENABLED') {
     return SCHEDULE_ACTIONS_DISABLED_REASONS_CONFIG[
       actionEnabledConfig ?? 'DISABLED_DEFAULT'
     ];
   }
   
   if (!actionRunnableStatus) {
     return undefined;
   }
   
   if (actionRunnableStatus !== 'RUNNABLE') {
     return SCHEDULE_ACTIONS_NON_RUNNABLE_REASONS_CONFIG[actionRunnableStatus];
   }
   
   return undefined;

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@Assem-Uber Assem-Uber force-pushed the feat/schedule-actions-pause-resume-ui branch from bba54e7 to e044172 Compare July 1, 2026 08:40
Assem-Uber and others added 5 commits July 1, 2026 10:41
Introduce schedule-actions types (including optional modal banner config),
pause/resume action config, disabled-reason helpers, and popover menu.
Disable menu actions while schedule data is loading.

Modal rendering and page wiring follow in stacked PRs.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Assem-Uber Assem-Uber force-pushed the feat/schedule-actions-pause-resume-ui branch from e044172 to 259e2e7 Compare July 1, 2026 08:45
@gitar-bot

gitar-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 2 resolved / 2 findings

Introduces the base infrastructure for schedule actions including types, configuration, and a popover menu. The previously identified issues where actions remained enabled during load states have been resolved.

✅ 2 resolved
Edge Case: Both actions appear enabled while schedule is still loading

📄 src/views/schedule-actions/schedule-actions-menu/schedule-actions-menu.tsx:17-21 📄 src/views/schedule-actions/schedule-actions-menu/helpers/get-action-disabled-reason.ts:9-11
In schedule-actions-menu.tsx, the disabled reason is computed only when schedule is defined: actionRunnableStatus: schedule ? action.getRunnableStatus(schedule) : undefined. When schedule is undefined (still loading or transiently empty), getActionDisabledReason returns undefined, so BOTH the Pause and Resume buttons render as enabled — a state that should never be reachable since the two actions are meant to be mutually exclusive based on state.paused. The popover trigger button uses isLoading, which mitigates this in the common case, but if the menu is opened before describe data resolves the user could trigger an action against unknown schedule state. Consider disabling actions (or showing a generic loading/disabled reason) when schedule is undefined.

Bug: Config-disabled action appears enabled while schedule loads

📄 src/views/schedule-actions/schedule-actions-menu/helpers/get-action-disabled-reason.ts:14-26 📄 src/views/schedule-actions/schedule-actions-menu/schedule-actions-menu.tsx:18-23
In getActionDisabledReason, the if (!actionRunnableStatus) return undefined; early-return runs before the actionEnabledConfig !== 'ENABLED' check. In the menu (schedule-actions-menu.tsx:20-22), actionRunnableStatus is undefined whenever schedule is still loading. actionsEnabledConfig comes from a separate resolver (SCHEDULE_ACTIONS_ENABLED) and can already be resolved to a disabled value (e.g. DISABLED_UNAUTHORIZED / DISABLED_DEFAULT) while the schedule is still loading.

Result: during loading, an action that config says is disabled/unauthorized returns undefined (no disabled reason) → the button is rendered enabled and clickable (onClick={() => onActionSelect(action)}). A user could trigger an action they are not authorized for, or that is globally disabled, during the loading window. It also silently drops the disabled tooltip in that window.

The existing test returns disabled reason over non-runnable reason when both apply establishes that config-disabled is intended to take precedence, but that precedence is bypassed when actionRunnableStatus is undefined. No test covers the loading (actionRunnableStatus: undefined) + config-disabled combination.

Fix: evaluate the config-disabled check before the runnable-status guard so a disabled config is always honored regardless of load state.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Comment thread src/config/dynamic/resolvers/schedule-actions-enabled.ts
Comment thread src/views/schedule-actions/schedule-actions.types.ts Outdated
@Assem-Uber Assem-Uber changed the title feat(schedule-actions): add action types, config, and menu (PR1) feat(schedule-actions): add action types, config, and menu Jul 1, 2026
@gitar-bot

gitar-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 2 resolved / 2 findings

Introduces the base infrastructure for schedule actions including types, configuration, and a popover menu. The previously identified issues where actions remained enabled during load states have been resolved.

✅ 2 resolved
Edge Case: Both actions appear enabled while schedule is still loading

📄 src/views/schedule-actions/schedule-actions-menu/schedule-actions-menu.tsx:17-21 📄 src/views/schedule-actions/schedule-actions-menu/helpers/get-action-disabled-reason.ts:9-11
In schedule-actions-menu.tsx, the disabled reason is computed only when schedule is defined: actionRunnableStatus: schedule ? action.getRunnableStatus(schedule) : undefined. When schedule is undefined (still loading or transiently empty), getActionDisabledReason returns undefined, so BOTH the Pause and Resume buttons render as enabled — a state that should never be reachable since the two actions are meant to be mutually exclusive based on state.paused. The popover trigger button uses isLoading, which mitigates this in the common case, but if the menu is opened before describe data resolves the user could trigger an action against unknown schedule state. Consider disabling actions (or showing a generic loading/disabled reason) when schedule is undefined.

Bug: Config-disabled action appears enabled while schedule loads

📄 src/views/schedule-actions/schedule-actions-menu/helpers/get-action-disabled-reason.ts:14-26 📄 src/views/schedule-actions/schedule-actions-menu/schedule-actions-menu.tsx:18-23
In getActionDisabledReason, the if (!actionRunnableStatus) return undefined; early-return runs before the actionEnabledConfig !== 'ENABLED' check. In the menu (schedule-actions-menu.tsx:20-22), actionRunnableStatus is undefined whenever schedule is still loading. actionsEnabledConfig comes from a separate resolver (SCHEDULE_ACTIONS_ENABLED) and can already be resolved to a disabled value (e.g. DISABLED_UNAUTHORIZED / DISABLED_DEFAULT) while the schedule is still loading.

Result: during loading, an action that config says is disabled/unauthorized returns undefined (no disabled reason) → the button is rendered enabled and clickable (onClick={() => onActionSelect(action)}). A user could trigger an action they are not authorized for, or that is globally disabled, during the loading window. It also silently drops the disabled tooltip in that window.

The existing test returns disabled reason over non-runnable reason when both apply establishes that config-disabled is intended to take precedence, but that precedence is bypassed when actionRunnableStatus is undefined. No test covers the loading (actionRunnableStatus: undefined) + config-disabled combination.

Fix: evaluate the config-disabled check before the runnable-status guard so a disabled config is always honored regardless of load state.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@Assem-Uber Assem-Uber merged commit 2a5aa87 into cadence-workflow:master Jul 1, 2026
4 checks passed
@Assem-Uber Assem-Uber deleted the feat/schedule-actions-pause-resume-ui branch July 1, 2026 14:55
Assem-Uber added a commit that referenced this pull request Jul 2, 2026
## Summary

Mounts the schedule-actions popover and confirmation modal on the
schedule page tab bar. Completes the stacked schedule-actions UI started
in #1419 and #1426.
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.

2 participants