| import React, { useState } from "react"; |
|
|
| |
| |
| |
| |
| |
| |
| export default function CreatePRButton({ |
| repo, |
| sessionId, |
| branch, |
| defaultBranch, |
| disabled, |
| onPRCreated, |
| }) { |
| const [creating, setCreating] = useState(false); |
| const [prUrl, setPrUrl] = useState(null); |
| const [error, setError] = useState(null); |
|
|
| const handleCreate = async () => { |
| if (!repo || !branch || branch === defaultBranch) return; |
|
|
| setCreating(true); |
| setError(null); |
|
|
| try { |
| const token = localStorage.getItem("github_token"); |
| const headers = { |
| "Content-Type": "application/json", |
| ...(token ? { Authorization: `Bearer ${token}` } : {}), |
| }; |
|
|
| const owner = repo.full_name?.split("/")[0] || repo.owner; |
| const name = repo.full_name?.split("/")[1] || repo.name; |
|
|
| const res = await fetch(`/api/repos/${owner}/${name}/pulls`, { |
| method: "POST", |
| headers, |
| body: JSON.stringify({ |
| title: `[GitPilot] Changes from session ${sessionId ? sessionId.slice(0, 8) : branch}`, |
| head: branch, |
| base: defaultBranch || "main", |
| body: [ |
| "## Summary", |
| "", |
| `Changes created by GitPilot AI assistant on branch \`${branch}\`.`, |
| "", |
| sessionId ? `Session ID: \`${sessionId}\`` : "", |
| "", |
| "---", |
| "*This PR was generated by [GitPilot](https://github.com/ruslanmv/gitpilot).*", |
| ] |
| .filter(Boolean) |
| .join("\n"), |
| }), |
| }); |
|
|
| const data = await res.json(); |
| if (!res.ok) throw new Error(data.detail || "Failed to create PR"); |
|
|
| const url = data.html_url || data.url; |
| setPrUrl(url); |
| onPRCreated?.({ pr_url: url, pr_number: data.number, branch }); |
| } catch (err) { |
| setError(err.message); |
| } finally { |
| setCreating(false); |
| } |
| }; |
|
|
| if (prUrl) { |
| return ( |
| <a |
| href={prUrl} |
| target="_blank" |
| rel="noreferrer" |
| style={styles.prLink} |
| > |
| <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> |
| <circle cx="18" cy="18" r="3" /> |
| <circle cx="6" cy="6" r="3" /> |
| <path d="M13 6h3a2 2 0 0 1 2 2v7" /> |
| <line x1="6" y1="9" x2="6" y2="21" /> |
| </svg> |
| View PR on GitHub → |
| </a> |
| ); |
| } |
|
|
| return ( |
| <div> |
| <button |
| type="button" |
| style={{ |
| ...styles.btn, |
| opacity: disabled || creating ? 0.55 : 1, |
| cursor: disabled || creating ? "not-allowed" : "pointer", |
| }} |
| onClick={handleCreate} |
| disabled={disabled || creating || !branch || branch === defaultBranch} |
| title={ |
| !branch || branch === defaultBranch |
| ? "Create a session branch first" |
| : "Create a pull request from session changes" |
| } |
| > |
| <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> |
| <circle cx="18" cy="18" r="3" /> |
| <circle cx="6" cy="6" r="3" /> |
| <path d="M13 6h3a2 2 0 0 1 2 2v7" /> |
| <line x1="6" y1="9" x2="6" y2="21" /> |
| </svg> |
| {creating ? "Creating PR..." : "Create PR"} |
| </button> |
| {error && ( |
| <div style={styles.error}>{error}</div> |
| )} |
| </div> |
| ); |
| } |
|
|
| const styles = { |
| btn: { |
| display: "flex", |
| alignItems: "center", |
| gap: 6, |
| height: 38, |
| padding: "0 14px", |
| borderRadius: 8, |
| border: "1px solid rgba(16, 185, 129, 0.3)", |
| background: "rgba(16, 185, 129, 0.08)", |
| color: "#10B981", |
| fontSize: 13, |
| fontWeight: 600, |
| cursor: "pointer", |
| whiteSpace: "nowrap", |
| transition: "background-color 0.15s", |
| }, |
| prLink: { |
| display: "flex", |
| alignItems: "center", |
| gap: 6, |
| height: 38, |
| padding: "0 14px", |
| borderRadius: 8, |
| background: "rgba(16, 185, 129, 0.10)", |
| color: "#10B981", |
| fontSize: 13, |
| fontWeight: 600, |
| textDecoration: "none", |
| whiteSpace: "nowrap", |
| }, |
| error: { |
| fontSize: 11, |
| color: "#EF4444", |
| marginTop: 4, |
| }, |
| }; |
|
|