Skip to content

Commit bbb2e5b

Browse files
committed
Add task reorder controls to repo form
1 parent 1e88778 commit bbb2e5b

1 file changed

Lines changed: 77 additions & 15 deletions

File tree

components/modals/RepoFormModal.tsx

Lines changed: 77 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)