@@ -1021,43 +1021,79 @@ interface TaskListItemProps {
10211021 onSelect : ( taskId : string ) => void ;
10221022 onDelete : ( taskId : string ) => void ;
10231023 onDuplicate : ( taskId : string ) => void ;
1024+ onMove : ( taskId : string , direction : 'up' | 'down' ) => void ;
1025+ canMoveUp : boolean ;
1026+ canMoveDown : boolean ;
10241027}
10251028
1026- const TaskListItem : React . FC < TaskListItemProps > = ( { task, isSelected, onSelect, onDelete, onDuplicate } ) => {
1029+ const TaskListItem : React . FC < TaskListItemProps > = ( { task, isSelected, onSelect, onDelete, onDuplicate, onMove , canMoveUp , canMoveDown } ) => {
10271030 const deleteTooltip = useTooltip ( 'Delete Task' ) ;
10281031 const duplicateTooltip = useTooltip ( 'Duplicate Task' ) ;
1032+ const moveUpTooltip = useTooltip ( 'Move Task Up' ) ;
1033+ const moveDownTooltip = useTooltip ( 'Move Task Down' ) ;
1034+
1035+ const moveButtonBaseClass = 'p-1 rounded-full focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500' ;
1036+ const getMoveButtonClass = ( canMove : boolean ) =>
1037+ canMove
1038+ ? `${ moveButtonBaseClass } text-gray-500 hover:text-blue-600 hover:bg-blue-100 dark:hover:bg-blue-900/50`
1039+ : `${ moveButtonBaseClass } text-gray-300 cursor-not-allowed` ;
10291040
10301041 const handleDelete = ( e : React . MouseEvent ) => {
10311042 e . stopPropagation ( ) ;
10321043 onDelete ( task . id ) ;
10331044 } ;
1034-
1045+
10351046 const handleDuplicate = ( e : React . MouseEvent ) => {
10361047 e . stopPropagation ( ) ;
10371048 onDuplicate ( task . id ) ;
10381049 } ;
10391050
1051+ const handleMove = ( e : React . MouseEvent , direction : 'up' | 'down' ) => {
1052+ e . stopPropagation ( ) ;
1053+ onMove ( task . id , direction ) ;
1054+ } ;
1055+
10401056 return (
10411057 < li className = { isSelected ? 'bg-blue-500/10' : '' } >
10421058 < button type = "button" onClick = { ( ) => onSelect ( task . id ) } className = "w-full text-left px-3 py-2 group" >
10431059 < div className = "flex justify-between items-start" >
10441060 < p className = { `font-medium group-hover:text-blue-600 dark:group-hover:text-blue-400 ${ isSelected ? 'text-blue-700 dark:text-blue-400' : 'text-gray-800 dark:text-gray-200' } ` } > { task . name } </ p >
1045- < div className = "flex items-center opacity-0 group-hover:opacity-100" >
1061+ < div className = "flex items-center gap-0.5 opacity-0 group-hover:opacity-100" >
1062+ < button
1063+ { ...moveUpTooltip }
1064+ type = "button"
1065+ onClick = { ( e ) => handleMove ( e , 'up' ) }
1066+ disabled = { ! canMoveUp }
1067+ aria-label = "Move task up"
1068+ className = { getMoveButtonClass ( canMoveUp ) }
1069+ >
1070+ < ArrowUpIcon className = "h-4 w-4" />
1071+ </ button >
1072+ < button
1073+ { ...moveDownTooltip }
1074+ type = "button"
1075+ onClick = { ( e ) => handleMove ( e , 'down' ) }
1076+ disabled = { ! canMoveDown }
1077+ aria-label = "Move task down"
1078+ className = { getMoveButtonClass ( canMoveDown ) }
1079+ >
1080+ < ArrowDownIcon className = "h-4 w-4" />
1081+ </ button >
10461082 < button
1047- { ...duplicateTooltip }
1048- type = "button"
1049- onClick = { handleDuplicate }
1050- className = "p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-100 dark:hover:bg-blue-900/50"
1083+ { ...duplicateTooltip }
1084+ type = "button"
1085+ onClick = { handleDuplicate }
1086+ className = "p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-100 dark:hover:bg-blue-900/50"
10511087 >
1052- < DocumentDuplicateIcon className = "h-4 w-4" />
1088+ < DocumentDuplicateIcon className = "h-4 w-4" />
10531089 </ button >
10541090 < button
1055- { ...deleteTooltip }
1056- type = "button"
1057- onClick = { handleDelete }
1058- className = "p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-100 dark:hover:bg-red-900/50"
1091+ { ...deleteTooltip }
1092+ type = "button"
1093+ onClick = { handleDelete }
1094+ className = "p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-100 dark:hover:bg-red-900/50"
10591095 >
1060- < TrashIcon className = "h-4 w-4" />
1096+ < TrashIcon className = "h-4 w-4" />
10611097 </ button >
10621098 </ div >
10631099 </ div >
@@ -1464,7 +1500,7 @@ const RepoEditView: React.FC<RepoEditViewProps> = ({ onSave, onCancel, repositor
14641500 const handleDuplicateTask = ( taskId : string ) => {
14651501 const taskToDuplicate = formData . tasks ?. find ( t => t . id === taskId ) ;
14661502 if ( ! taskToDuplicate ) return ;
1467-
1503+
14681504 // Deep copy, assign new IDs to task and steps
14691505 const newTask = {
14701506 ...JSON . parse ( JSON . stringify ( taskToDuplicate ) ) ,
@@ -1488,6 +1524,29 @@ const RepoEditView: React.FC<RepoEditViewProps> = ({ onSave, onCancel, repositor
14881524 setSelectedTaskId ( newTask . id ) ;
14891525 } ;
14901526
1527+ const handleMoveTask = ( taskId : string , direction : 'up' | 'down' ) => {
1528+ setFormData ( prev => {
1529+ const tasks = prev . tasks || [ ] ;
1530+ const currentIndex = tasks . findIndex ( t => t . id === taskId ) ;
1531+ if ( currentIndex === - 1 ) {
1532+ return prev ;
1533+ }
1534+
1535+ const targetIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1 ;
1536+ if ( targetIndex < 0 || targetIndex >= tasks . length ) {
1537+ return prev ;
1538+ }
1539+
1540+ const newTasks = [ ...tasks ] ;
1541+ const [ movedTask ] = newTasks . splice ( currentIndex , 1 ) ;
1542+ newTasks . splice ( targetIndex , 0 , movedTask ) ;
1543+
1544+ return { ...prev , tasks : newTasks } ;
1545+ } ) ;
1546+
1547+ setSelectedTaskId ( prevSelected => ( prevSelected === taskId ? taskId : prevSelected ) ) ;
1548+ } ;
1549+
14911550 const handleAddWebLink = ( ) => {
14921551 const newLink : WebLinkConfig = { id : `wl_${ Date . now ( ) } ` , name : 'New Link' , url : 'https://' } ;
14931552 setFormData ( prev => ( { ...prev , webLinks : [ ...( prev . webLinks || [ ] ) , newLink ] } ) ) ;
@@ -1839,14 +1898,17 @@ const RepoEditView: React.FC<RepoEditViewProps> = ({ onSave, onCancel, repositor
18391898 </ button >
18401899 </ div >
18411900 < ul className = "flex-1 overflow-y-auto" >
1842- { ( formData . tasks || [ ] ) . map ( task => (
1901+ { ( formData . tasks || [ ] ) . map ( ( task , index , array ) => (
18431902 < TaskListItem
18441903 key = { task . id }
18451904 task = { task }
18461905 isSelected = { selectedTaskId === task . id }
18471906 onSelect = { setSelectedTaskId }
18481907 onDelete = { handleDeleteTask }
18491908 onDuplicate = { handleDuplicateTask }
1909+ onMove = { handleMoveTask }
1910+ canMoveUp = { index > 0 }
1911+ canMoveDown = { index < array . length - 1 }
18501912 />
18511913 ) ) }
18521914 { ( formData . tasks || [ ] ) . length === 0 && (
0 commit comments