Make it possible to create draft PRs (vibe-kanban) (#1460)

* All checks pass. Let me summarize what was implemented:

## Summary

I've successfully implemented the draft PR feature. Here's what was changed:

### Backend Changes

1. **`crates/server/src/routes/task_attempts.rs`** (lines 70-76, 724-731):
   - Added `draft: Option<bool>` field to `CreateGitHubPrRequest` struct
   - Pass the draft field to the `CreatePrRequest` when creating PRs

2. **`crates/services/src/services/github.rs`** (lines 146-153):
   - Added `draft: Option<bool>` field to `CreatePrRequest` struct

3. **`crates/services/src/services/github/cli.rs`** (lines 158-160):
   - Added logic to pass `--draft` flag to `gh pr create` when draft is true

### Frontend Changes

4. **`frontend/src/components/dialogs/tasks/CreatePRDialog.tsx`**:
   - Added `Checkbox` import
   - Added `isDraft` state (default: false)
   - Added checkbox UI with label from translations
   - Pass `draft: isDraft` in the API call
   - Reset `isDraft` on success and cancel

### Type Generation

5. **`shared/types.ts`** (auto-generated):
   - `CreateGitHubPrRequest` now includes `draft: boolean | null`

### Translations

6. Added `"draftLabel"` translation key to all locale files:
   - `frontend/src/i18n/locales/en/tasks.json`: "Create as draft"
   - `frontend/src/i18n/locales/es/tasks.json`: "Crear como borrador"
   - `frontend/src/i18n/locales/ja/tasks.json`: "下書きとして作成"
   - `frontend/src/i18n/locales/ko/tasks.json`: "초안으로 만들기"
   - `frontend/src/i18n/locales/zh-Hans/tasks.json`: "创建为草稿"

* Done. I've updated the checkbox to use `h-5 w-5` (20px) and added `text-sm` to the label for consistent sizing.
This commit is contained in:
Louis Knight-Webb
2025-12-08 16:53:37 +00:00
committed by GitHub
parent 047695e554
commit 9c70858b21
10 changed files with 30 additions and 1 deletions

View File

@@ -72,6 +72,7 @@ pub struct CreateGitHubPrRequest {
pub title: String,
pub body: Option<String>,
pub target_branch: Option<String>,
pub draft: Option<bool>,
}
#[derive(Debug, Deserialize)]
@@ -726,6 +727,7 @@ pub async fn create_github_pr(
body: request.body.clone(),
head_branch: task_attempt.branch.clone(),
base_branch: norm_target_branch_name.clone(),
draft: request.draft,
};
// Use GitService to get the remote URL, then create GitHubRepoInfo
let repo_info = deployment

View File

@@ -149,6 +149,7 @@ pub struct CreatePrRequest {
pub body: Option<String>,
pub head_branch: String,
pub base_branch: String,
pub draft: Option<bool>,
}
#[derive(Debug, Clone)]

View File

@@ -155,6 +155,10 @@ impl GhCli {
args.push(OsString::from("--body"));
args.push(OsString::from(body));
if request.draft.unwrap_or(false) {
args.push(OsString::from("--draft"));
}
let raw = self.run(args)?;
Self::parse_pr_create_text(&raw)
}

View File

@@ -10,6 +10,7 @@ import { Label } from '@radix-ui/react-label';
import { Textarea } from '@/components/ui/textarea.tsx';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Checkbox } from '@/components/ui/checkbox';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import BranchSelector from '@/components/tasks/BranchSelector';
import { useCallback, useEffect, useMemo, useState } from 'react';
@@ -56,6 +57,7 @@ const CreatePRDialogImpl = NiceModal.create<CreatePRDialogProps>(
);
const [branches, setBranches] = useState<GitBranch[]>([]);
const [branchesLoading, setBranchesLoading] = useState(false);
const [isDraft, setIsDraft] = useState(false);
const getGhCliHelpTitle = (variant: GhCliSupportVariant) =>
variant === 'homebrew'
@@ -136,12 +138,14 @@ const CreatePRDialogImpl = NiceModal.create<CreatePRDialogProps>(
title: prTitle,
body: prBody || null,
target_branch: prBaseBranch || null,
draft: isDraft,
});
if (result.success) {
setPrTitle('');
setPrBody('');
setPrBaseBranch('');
setIsDraft(false);
setCreatingPR(false);
modal.hide();
return;
@@ -213,6 +217,7 @@ const CreatePRDialogImpl = NiceModal.create<CreatePRDialogProps>(
prBaseBranch,
prBody,
prTitle,
isDraft,
modal,
isMacEnvironment,
t,
@@ -224,6 +229,7 @@ const CreatePRDialogImpl = NiceModal.create<CreatePRDialogProps>(
setPrTitle('');
setPrBody('');
setPrBaseBranch('');
setIsDraft(false);
}, [modal]);
return (
@@ -286,6 +292,17 @@ const CreatePRDialogImpl = NiceModal.create<CreatePRDialogProps>(
}
/>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="pr-draft"
checked={isDraft}
onCheckedChange={setIsDraft}
className="h-5 w-5"
/>
<Label htmlFor="pr-draft" className="cursor-pointer text-sm">
{t('createPrDialog.draftLabel')}
</Label>
</div>
{ghCliHelp?.variant && (
<Alert variant="default">
<AlertTitle>

View File

@@ -355,6 +355,7 @@
"baseBranchLabel": "Base Branch",
"loadingBranches": "Loading branches...",
"selectBaseBranch": "Select base branch",
"draftLabel": "Create as draft",
"creating": "Creating...",
"createButton": "Create PR",
"errors": {

View File

@@ -112,6 +112,7 @@
"baseBranchLabel": "Rama Base",
"loadingBranches": "Cargando ramas...",
"selectBaseBranch": "Seleccionar rama base",
"draftLabel": "Crear como borrador",
"creating": "Creando...",
"createButton": "Crear PR",
"errors": {

View File

@@ -112,6 +112,7 @@
"baseBranchLabel": "ベースブランチ",
"loadingBranches": "ブランチを読み込み中...",
"selectBaseBranch": "ベースブランチを選択",
"draftLabel": "下書きとして作成",
"creating": "作成中...",
"createButton": "PRを作成",
"errors": {

View File

@@ -112,6 +112,7 @@
"baseBranchLabel": "기본 브랜치",
"loadingBranches": "브랜치 로딩 중...",
"selectBaseBranch": "기본 브랜치 선택",
"draftLabel": "초안으로 만들기",
"creating": "생성 중...",
"createButton": "PR 생성",
"errors": {

View File

@@ -355,6 +355,7 @@
"baseBranchLabel": "基础分支",
"loadingBranches": "加载分支中...",
"selectBaseBranch": "选择基础分支",
"draftLabel": "创建为草稿",
"creating": "创建中...",
"createButton": "创建 PR",
"errors": {

View File

@@ -216,7 +216,7 @@ export type ShareTaskResponse = { shared_task_id: string, };
export type CreateAndStartTaskRequest = { task: CreateTask, executor_profile_id: ExecutorProfileId, base_branch: string, };
export type CreateGitHubPrRequest = { title: string, body: string | null, target_branch: string | null, };
export type CreateGitHubPrRequest = { title: string, body: string | null, target_branch: string | null, draft: boolean | null, };
export type ImageResponse = { id: string, file_path: string, original_name: string, mime_type: string | null, size_bytes: bigint, hash: string, created_at: string, updated_at: string, };