diff --git a/src/components/GitPanel.jsx b/src/components/GitPanel.jsx index 8dc9118..57b4fa2 100755 --- a/src/components/GitPanel.jsx +++ b/src/components/GitPanel.jsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useRef } from 'react'; -import { GitBranch, GitCommit, Plus, Minus, RefreshCw, Check, X, ChevronDown, ChevronRight, Info, History, FileText, Mic, MicOff, Sparkles, Download, RotateCcw, Trash2, AlertTriangle } from 'lucide-react'; +import { GitBranch, GitCommit, Plus, Minus, RefreshCw, Check, X, ChevronDown, ChevronRight, Info, History, FileText, Mic, MicOff, Sparkles, Download, RotateCcw, Trash2, AlertTriangle, Upload } from 'lucide-react'; import { MicButton } from './MicButton.jsx'; import { authenticatedFetch } from '../utils/api'; @@ -27,8 +27,9 @@ function GitPanel({ selectedProject, isMobile }) { const [remoteStatus, setRemoteStatus] = useState(null); const [isFetching, setIsFetching] = useState(false); const [isPulling, setIsPulling] = useState(false); + const [isPushing, setIsPushing] = useState(false); const [isCommitAreaCollapsed, setIsCommitAreaCollapsed] = useState(isMobile); // Collapsed by default on mobile - const [confirmAction, setConfirmAction] = useState(null); // { type: 'discard|commit|pull', file?: string, message?: string } + const [confirmAction, setConfirmAction] = useState(null); // { type: 'discard|commit|pull|push', file?: string, message?: string } const textareaRef = useRef(null); const dropdownRef = useRef(null); @@ -238,6 +239,33 @@ function GitPanel({ selectedProject, isMobile }) { } }; + const handlePush = async () => { + setIsPushing(true); + try { + const response = await authenticatedFetch('/api/git/push', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + project: selectedProject.name + }) + }); + + const data = await response.json(); + if (data.success) { + // Refresh status after successful push + fetchGitStatus(); + fetchRemoteStatus(); + } else { + console.error('Push failed:', data.error); + // TODO: Show user-friendly error message + } + } catch (error) { + console.error('Error pushing to remote:', error); + } finally { + setIsPushing(false); + } + }; + const discardChanges = async (filePath) => { try { const response = await authenticatedFetch('/api/git/discard', { @@ -283,6 +311,9 @@ function GitPanel({ selectedProject, isMobile }) { case 'pull': await handlePull(); break; + case 'push': + await handlePush(); + break; } } catch (error) { console.error(`Error executing ${type}:`, error); @@ -702,6 +733,22 @@ function GitPanel({ selectedProject, isMobile }) { )} + {/* Push button - show when ahead (primary action when ahead only) */} + {remoteStatus.ahead > 0 && ( + + )} + {/* Fetch button - show when ahead only or when diverged (secondary action) */} {(remoteStatus.ahead > 0 || (remoteStatus.behind > 0 && remoteStatus.ahead > 0)) && (