From 3551fe0238efdc779ca19acb0608fa4f4f5d4912 Mon Sep 17 00:00:00 2001 From: Mitch Talmadge Date: Tue, 14 Oct 2025 07:57:33 +0000 Subject: [PATCH] Add issue transition options for Start Work page (#502) --- package-lock.json | 35 --------- package.json | 12 ++++ src/config/model.ts | 4 ++ src/lib/ipc/toUI/startWork.ts | 2 + .../startWorkWebviewController.test.ts | 18 +++++ src/react/atlascode/common/StartWorkPanel.tsx | 19 ++++- .../atlascode/config/StartWorkSettings.tsx | 72 ++++++++++++++++++- .../advancedConfigs/advancedConfigsPanel.tsx | 6 ++ .../subpanels/startWork/StartWorkPanel.tsx | 19 ++++- src/react/atlascode/config/jira/JiraPanel.tsx | 6 ++ .../atlascode/startwork/StartWorkPage.tsx | 12 +++- .../startwork/startWorkController.ts | 2 + .../startwork/vscStartWorkActionApi.ts | 2 + 13 files changed, 167 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index ae3ecd615..7fec4cace 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9419,41 +9419,6 @@ "react": "^18.2.0" } }, - "node_modules/@atlaskit/editor-plugin-loom/node_modules/@atlaskit/icon-lab/node_modules/@atlaskit/icon": { - "version": "26.4.1", - "resolved": "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/icon/-/icon-26.4.1.tgz", - "integrity": "sha512-JyLx9LYF8rYNzcyXUCMDvLqRXtjlzHoIUV7tjFA38TTBR39KFCfqnyes5fMs+u75mBbHnXAnZV+G8cArmbVotQ==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@atlaskit/platform-feature-flags": "^1.1.0", - "@atlaskit/tokens": "^5.0.0", - "@babel/register": "^7.25.9", - "@babel/runtime": "^7.0.0", - "@compiled/react": "^0.18.3" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/@atlaskit/editor-plugin-loom/node_modules/@atlaskit/icon-lab/node_modules/@atlaskit/tokens": { - "version": "5.6.3", - "resolved": "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/tokens/-/tokens-5.6.3.tgz", - "integrity": "sha512-oRYrJtzWFzA7ExUHXhFI2h5pM70eXKkOZVj29Ase1ZSCSqN54Ii2KrE69U43pkQzVT05KTq0kV3Uoq3MM6HP3Q==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@atlaskit/ds-lib": "^5.0.0", - "@atlaskit/platform-feature-flags": "^1.1.0", - "@babel/runtime": "^7.0.0", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.20.0", - "bind-event-listener": "^3.0.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, "node_modules/@atlaskit/editor-plugin-loom/node_modules/@atlaskit/react-ufo": { "version": "3.14.15", "resolved": "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/react-ufo/-/react-ufo-3.14.15.tgz", diff --git a/package.json b/package.json index b31b6d43a..9d6c4f60b 100644 --- a/package.json +++ b/package.json @@ -1116,6 +1116,18 @@ "description": "Custom prefixes for branch names on start work screen", "scope": "window" }, + "atlascode.jira.startWorkBranchTemplate.enableIssueTransition": { + "type": "boolean", + "default": true, + "description": "Enables the issue transition toggle on start work screen", + "scope": "window" + }, + "atlascode.jira.startWorkBranchTemplate.defaultTransitionName": { + "type": "string", + "default": "", + "description": "The default transition name to use when starting work on an issue", + "scope": "window" + }, "atlascode.jira.showCreateIssueProblems": { "type": "boolean", "default": false, diff --git a/src/config/model.ts b/src/config/model.ts index 394767a1a..5abb1f338 100644 --- a/src/config/model.ts +++ b/src/config/model.ts @@ -90,6 +90,8 @@ export interface JiraExplorer { export interface StartWorkBranchTemplate { customPrefixes: string[]; customTemplate: string; + enableIssueTransition: boolean; + defaultTransitionName: string; } export interface JiraHover { @@ -202,6 +204,8 @@ const emptyTodoIssues: TodoIssues = { const emptyStartWorkBranchTemplate: StartWorkBranchTemplate = { customPrefixes: [], customTemplate: '{{prefix}}/{{issueKey}}-{{summary}}', + enableIssueTransition: true, + defaultTransitionName: '', }; const emptyJiraConfig: JiraConfig = { diff --git a/src/lib/ipc/toUI/startWork.ts b/src/lib/ipc/toUI/startWork.ts index d37feba92..5042050e3 100644 --- a/src/lib/ipc/toUI/startWork.ts +++ b/src/lib/ipc/toUI/startWork.ts @@ -26,6 +26,8 @@ export interface StartWorkInitMessage { customTemplate: string; customPrefixes: string[]; isRovoDevEnabled: boolean; + enableIssueTransition: boolean; + defaultTransitionName: string; } export interface StartWorkResponseMessage { diff --git a/src/lib/webview/controller/startwork/startWorkWebviewController.test.ts b/src/lib/webview/controller/startwork/startWorkWebviewController.test.ts index ffe9965f5..3ea257325 100644 --- a/src/lib/webview/controller/startwork/startWorkWebviewController.test.ts +++ b/src/lib/webview/controller/startwork/startWorkWebviewController.test.ts @@ -120,6 +120,8 @@ describe('StartWorkWebviewController', () => { customTemplate: '{issueKey}', customPrefixes: ['feature/', 'bugfix/'], isRovoDevEnabled: true, + enableIssueTransition: true, + defaultTransitionName: '', }; beforeEach(() => { @@ -501,6 +503,8 @@ describe('StartWorkWebviewController', () => { mockApi.getStartWorkConfig.mockReturnValue({ customTemplate: '{issueKey}', customPrefixes: ['feature/', 'bugfix/'], + enableIssueTransition: true, + defaultTransitionName: '', }); }); @@ -534,6 +538,8 @@ describe('StartWorkWebviewController', () => { ]), customTemplate: '{issueKey}', customPrefixes: ['feature/', 'bugfix/'], + enableIssueTransition: true, + defaultTransitionName: '', }); }); @@ -566,6 +572,8 @@ describe('StartWorkWebviewController', () => { ]), customTemplate: '{issueKey}', customPrefixes: ['feature/', 'bugfix/'], + enableIssueTransition: true, + defaultTransitionName: '', }); }); @@ -582,6 +590,8 @@ describe('StartWorkWebviewController', () => { repoData: [], customTemplate: '{issueKey}', customPrefixes: ['feature/', 'bugfix/'], + enableIssueTransition: true, + defaultTransitionName: '', }); }); @@ -679,6 +689,8 @@ describe('StartWorkWebviewController', () => { mockApi.getStartWorkConfig.mockReturnValue({ customTemplate: '{issueKey}', customPrefixes: [], + enableIssueTransition: true, + defaultTransitionName: '', }); await controller.onMessageReceived({ type: CommonActionType.Refresh }); @@ -693,6 +705,8 @@ describe('StartWorkWebviewController', () => { ]), customTemplate: '{issueKey}', customPrefixes: [], + enableIssueTransition: true, + defaultTransitionName: '', }); }); @@ -729,6 +743,8 @@ describe('StartWorkWebviewController', () => { mockApi.getStartWorkConfig.mockReturnValue({ customTemplate: '{issueKey}', customPrefixes: [], + enableIssueTransition: true, + defaultTransitionName: '', }); await controller.onMessageReceived({ type: CommonActionType.Refresh }); @@ -743,6 +759,8 @@ describe('StartWorkWebviewController', () => { ]), customTemplate: '{issueKey}', customPrefixes: [], + enableIssueTransition: true, + defaultTransitionName: '', }); }); }); diff --git a/src/react/atlascode/common/StartWorkPanel.tsx b/src/react/atlascode/common/StartWorkPanel.tsx index f6bebfbc2..f4e568499 100644 --- a/src/react/atlascode/common/StartWorkPanel.tsx +++ b/src/react/atlascode/common/StartWorkPanel.tsx @@ -13,10 +13,20 @@ import { PanelTitle } from './PanelTitle'; type StartWorkPanelProps = CommonSubpanelProps & { customPrefixes: string[]; customTemplate: string; + enableIssueTransition: boolean; + defaultTransitionName: string; }; export const StartWorkPanel: React.FunctionComponent = memo( - ({ visible, expanded, customPrefixes, customTemplate, onSubsectionChange }) => { + ({ + visible, + expanded, + customPrefixes, + customTemplate, + defaultTransitionName, + enableIssueTransition, + onSubsectionChange, + }) => { const [internalExpanded, setInternalExpanded] = useState(expanded); const expansionHandler = useCallback( @@ -47,7 +57,12 @@ export const StartWorkPanel: React.FunctionComponent = memo configure the start work screen - + ); diff --git a/src/react/atlascode/config/StartWorkSettings.tsx b/src/react/atlascode/config/StartWorkSettings.tsx index 9c48efa55..50aed38aa 100644 --- a/src/react/atlascode/config/StartWorkSettings.tsx +++ b/src/react/atlascode/config/StartWorkSettings.tsx @@ -1,5 +1,5 @@ import { InlineTextEditor, InlineTextEditorList } from '@atlassianlabs/guipi-core-components'; -import { Box, FormHelperText, Grid, Link, Typography } from '@mui/material'; +import { Box, FormHelperText, Grid, Link, Switch, Tooltip, Typography } from '@mui/material'; import Mustache from 'mustache'; import React, { useCallback, useContext, useEffect, useState } from 'react'; @@ -9,9 +9,16 @@ import { ConfigControllerContext } from './configController'; type StartWorkSettings = { customTemplate: string; customPrefixes: string[]; + enableIssueTransition: boolean; + defaultTransitionName: string; }; -export const StartWorkSettings: React.FunctionComponent = ({ customTemplate, customPrefixes }) => { +export const StartWorkSettings: React.FunctionComponent = ({ + customTemplate, + customPrefixes, + enableIssueTransition, + defaultTransitionName, +}) => { const controller = useContext(ConfigControllerContext); const boxClass = useBorderBoxStyles(); const [changes, setChanges] = useState<{ [key: string]: any }>({}); @@ -21,6 +28,10 @@ export const StartWorkSettings: React.FunctionComponent = ({ setTemplate(customTemplate); }, [customTemplate]); + const stopProp = useCallback((event: React.MouseEvent) => { + event.stopPropagation(); + }, []); + const handlePrefixesChange = useCallback((newOptions: string[]) => { const changes = Object.create(null); changes['jira.startWorkBranchTemplate.customPrefixes'] = newOptions; @@ -34,6 +45,25 @@ export const StartWorkSettings: React.FunctionComponent = ({ setChanges(changes); }, []); + const handleEnableIssueTransitionChange = useCallback((enable: boolean) => { + const changes = Object.create(null); + changes['jira.startWorkBranchTemplate.enableIssueTransition'] = enable; + setChanges(changes); + }, []); + + const toggleIssueTransition = useCallback( + (event: React.ChangeEvent) => { + handleEnableIssueTransitionChange(event.target.checked); + }, + [handleEnableIssueTransitionChange], + ); + + const handleTransitionNameChange = useCallback((name: string) => { + const changes = Object.create(null); + changes['jira.startWorkBranchTemplate.defaultTransitionName'] = name; + setChanges(changes); + }, []); + useEffect(() => { if (Object.keys(changes).length > 0) { controller.updateConfig(changes); @@ -145,6 +175,44 @@ export const StartWorkSettings: React.FunctionComponent = ({ + + + Enable Issue Transition + + If enabled, the "Transition issue" dropdown on the Start Work page will be shown. This allows + you to choose which status the issue should move to when starting work. + + + + + + + + + + + Default Issue Transition + + The "Transition issue" dropdown on the Start Work page lets you choose which status the issue + should move to. By default, it is set to "In Progress". To change the default selection in the + dropdown, enter a transition name below. + + + + + + ); }; diff --git a/src/react/atlascode/config/advancedConfigs/advancedConfigsPanel.tsx b/src/react/atlascode/config/advancedConfigs/advancedConfigsPanel.tsx index baff152ca..20c623e1b 100644 --- a/src/react/atlascode/config/advancedConfigs/advancedConfigsPanel.tsx +++ b/src/react/atlascode/config/advancedConfigs/advancedConfigsPanel.tsx @@ -85,6 +85,12 @@ export const AdvancedConfigsPanel: React.FunctionComponent onSubsectionChange={onSubsectionChange} customPrefixes={config[`${ConfigSection.Jira}.startWorkBranchTemplate.customPrefixes`]} customTemplate={config[`${ConfigSection.Jira}.startWorkBranchTemplate.customTemplate`]} + defaultTransitionName={ + config[`${ConfigSection.Jira}.startWorkBranchTemplate.defaultTransitionName`] + } + enableIssueTransition={ + config[`${ConfigSection.Jira}.startWorkBranchTemplate.enableIssueTransition`] + } /> diff --git a/src/react/atlascode/config/advancedConfigs/subpanels/startWork/StartWorkPanel.tsx b/src/react/atlascode/config/advancedConfigs/subpanels/startWork/StartWorkPanel.tsx index 9c0258dfa..fd60b8aa9 100644 --- a/src/react/atlascode/config/advancedConfigs/subpanels/startWork/StartWorkPanel.tsx +++ b/src/react/atlascode/config/advancedConfigs/subpanels/startWork/StartWorkPanel.tsx @@ -13,10 +13,20 @@ import { StartWorkSettings } from './../../../StartWorkSettings'; type StartWorkPanelProps = CommonSubpanelV3Props & { customPrefixes: string[]; customTemplate: string; + defaultTransitionName: string; + enableIssueTransition: boolean; }; export const StartWorkPanel: React.FunctionComponent = memo( - ({ visible, expanded, customPrefixes, customTemplate, onSubsectionChange }) => { + ({ + visible, + expanded, + customPrefixes, + customTemplate, + defaultTransitionName, + enableIssueTransition, + onSubsectionChange, + }) => { const [internalExpanded, setInternalExpanded] = useState(expanded); const expansionHandler = useCallback( @@ -47,7 +57,12 @@ export const StartWorkPanel: React.FunctionComponent = memo configure the start work screen - + ); diff --git a/src/react/atlascode/config/jira/JiraPanel.tsx b/src/react/atlascode/config/jira/JiraPanel.tsx index 4b967ebf2..cb2bda190 100644 --- a/src/react/atlascode/config/jira/JiraPanel.tsx +++ b/src/react/atlascode/config/jira/JiraPanel.tsx @@ -94,6 +94,12 @@ export const JiraPanel: React.FunctionComponent = ({ onSubsectionChange={onSubsectionChange} customPrefixes={config[`${ConfigSection.Jira}.startWorkBranchTemplate.customPrefixes`]} customTemplate={config[`${ConfigSection.Jira}.startWorkBranchTemplate.customTemplate`]} + enableIssueTransition={ + config[`${ConfigSection.Jira}.startWorkBranchTemplate.enableIssueTransition`] + } + defaultTransitionName={ + config[`${ConfigSection.Jira}.startWorkBranchTemplate.defaultTransitionName`] + } /> diff --git a/src/react/atlascode/startwork/StartWorkPage.tsx b/src/react/atlascode/startwork/StartWorkPage.tsx index 865b714b7..ac790c6eb 100644 --- a/src/react/atlascode/startwork/StartWorkPage.tsx +++ b/src/react/atlascode/startwork/StartWorkPage.tsx @@ -353,15 +353,25 @@ const StartWorkPage: React.FunctionComponent = () => { }); }, [controller]); + useEffect(() => { + setTransitionIssueEnabled(state.enableIssueTransition); + }, [state.enableIssueTransition]); + useEffect(() => { // best effort to default to a transition that will move the issue to `In progress` state const inProgressTransitionGuess: Transition = + (state.defaultTransitionName !== '' && + state.issue.transitions.find( + (t) => + !t.isInitial && + t.to.name.toLocaleLowerCase() === state.defaultTransitionName.toLocaleLowerCase(), + )) || state.issue.transitions.find((t) => !t.isInitial && t.to.name.toLocaleLowerCase().includes('progress')) || state.issue.transitions.find((t) => !t.isInitial) || state.issue.transitions?.[0] || emptyTransition; setTransition(inProgressTransitionGuess); - }, [state.issue]); + }, [state.defaultTransitionName, state.issue]); useEffect(() => { if (state.rovoDevPreference !== undefined) { diff --git a/src/react/atlascode/startwork/startWorkController.ts b/src/react/atlascode/startwork/startWorkController.ts index 14b7386ba..d305758f0 100644 --- a/src/react/atlascode/startwork/startWorkController.ts +++ b/src/react/atlascode/startwork/startWorkController.ts @@ -66,6 +66,8 @@ const emptyState: StartWorkState = { customTemplate: '{{prefix}}/{{issueKey}}-{{summary}}', customPrefixes: [], rovoDevPreference: false, + enableIssueTransition: true, + defaultTransitionName: '', }; enum StartWorkUIActionType { diff --git a/src/webview/startwork/vscStartWorkActionApi.ts b/src/webview/startwork/vscStartWorkActionApi.ts index bb9674254..b4433bcee 100644 --- a/src/webview/startwork/vscStartWorkActionApi.ts +++ b/src/webview/startwork/vscStartWorkActionApi.ts @@ -94,6 +94,8 @@ export class VSCStartWorkActionApi implements StartWorkActionApi { return { customTemplate: Container.config.jira.startWorkBranchTemplate.customTemplate, customPrefixes: Container.config.jira.startWorkBranchTemplate.customPrefixes, + enableIssueTransition: Container.config.jira.startWorkBranchTemplate.enableIssueTransition, + defaultTransitionName: Container.config.jira.startWorkBranchTemplate.defaultTransitionName, }; }